Overview
The kv.store() function provides access to a persistent key-value store unique to each guild. Store bot configuration, user data, and other persistent state.
Signature
function store(name: string): KvStore
Parameters
Name of the store namespace (e.g., ‘settings’, ‘userdata’)
Return Value
Returns a KvStore instance with methods for reading and writing data.
KvStore Methods
get()
Retrieve a value from the store.
async get(key: string): Promise<string | null>
Parameters:
key - The key to retrieve
Returns: The stored string value, or null if not found
Example:
const db = kv.store('settings')
const prefix = await db.get('prefix')
console.log(prefix) // '!' or null
Retrieve a value along with its metadata.
async getWithMetadata(key: string): Promise<{
value: string | null
metadata?: Record<string, unknown>
}>
Example:
const result = await db.getWithMetadata('premium_status')
console.log(result.value) // 'active'
console.log(result.metadata?.expiresAt) // '2024-12-31'
set()
Store a value in the database.
async set(
key: string,
value: string,
options?: {
expiration?: bigint
metadata?: JsonValue
}
): Promise<void>
Parameters:
key - The key to set
value - The value to store (max 1MB)
options.expiration - Optional Unix timestamp (seconds) when the key expires
options.metadata - Optional JSON metadata to attach
Example:
// Simple set
await db.set('prefix', '!')
// With expiration (expires in 1 hour)
const oneHourFromNow = BigInt(Math.floor(Date.now() / 1000) + 3600)
await db.set('temp_token', 'abc123', { expiration: oneHourFromNow })
// With metadata
await db.set('user_points', '100', {
metadata: { lastUpdated: new Date().toISOString() }
})
Update only the metadata for a key without changing its value.
async updateMetadata(key: string, metadata: JsonValue | undefined): Promise<void>
Example:
await db.updateMetadata('user_points', {
lastModified: new Date().toISOString(),
modifiedBy: 'admin'
})
delete()
Remove a key from the store.
async delete(key: string): Promise<void>
Example:
await db.delete('old_setting')
list()
List all keys in the store with pagination support.
async list(options?: {
prefix?: string
limit?: bigint
cursor?: string
}): Promise<{
keys: Array<{
name: string
expiration?: bigint
metadata?: JsonValue
}>
listComplete: boolean
cursor?: string
}>
Parameters:
options.prefix - Only return keys starting with this prefix
options.limit - Maximum keys to return (default: 100, max: 1000)
options.cursor - Pagination cursor from previous list() call
Returns:
keys - Array of key information objects
listComplete - Whether all matching keys were returned
cursor - Cursor for fetching the next page (if listComplete is false)
Example:
// List all keys
const result = await db.list()
for (const key of result.keys) {
console.log(key.name)
}
// List with prefix filter
const userKeys = await db.list({ prefix: 'user:' })
// Paginated listing
let cursor: string | undefined
do {
const page = await db.list({ limit: BigInt(100), cursor })
for (const key of page.keys) {
console.log(key.name)
}
cursor = page.cursor
} while (cursor)
Usage Examples
Simple Configuration Storage
const config = kv.store('config')
// Save settings
await config.set('welcome_channel', '123456789')
await config.set('log_channel', '987654321')
// Load settings
const welcomeChannel = await config.get('welcome_channel')
User Points System
const points = kv.store('points')
slash({
name: 'points',
description: 'Check your points',
run: async (ctx) => {
const userId = ctx.msg.user.id
const userPoints = await points.get(`user:${userId}`) ?? '0'
await ctx.reply(`You have ${userPoints} points`)
}
})
slash({
name: 'addpoints',
description: 'Award points to a user',
options: [
{ name: 'user', type: 'string', required: true },
{ name: 'amount', type: 'integer', required: true }
],
run: async (ctx) => {
const userId = ctx.options.user as string
const amount = ctx.options.amount as number
const current = parseInt(await points.get(`user:${userId}`) ?? '0')
const newTotal = current + amount
await points.set(`user:${userId}`, String(newTotal), {
metadata: {
lastUpdated: new Date().toISOString(),
awardedBy: ctx.msg.user.id
}
})
await ctx.reply(`Awarded ${amount} points! New total: ${newTotal}`)
}
})
Temporary Data with Expiration
const temp = kv.store('temporary')
// Store verification code that expires in 5 minutes
const fiveMinutes = BigInt(Math.floor(Date.now() / 1000) + 300)
await temp.set('verify:' + userId, verificationCode, {
expiration: fiveMinutes
})
// Check verification (will be null after expiration)
const code = await temp.get('verify:' + userId)
if (code === expectedCode) {
await ctx.reply('Verified!')
}
List and Cleanup
const cache = kv.store('cache')
// Delete all keys with a certain prefix
const result = await cache.list({ prefix: 'old:' })
for (const key of result.keys) {
await cache.delete(key.name)
}
console.log(`Deleted ${result.keys.length} old entries`)
Limits
- Value size: Maximum 1MB per value
- Keys per list: Default 100, maximum 1000 per request
- Key naming: Any string (recommended: use prefixes like
user:123, config:prefix)
Storage Scope
- Each store name creates a separate namespace
- All data is scoped per guild automatically
- Different guilds cannot access each other’s data
- Store names are case-sensitive
Notes
- Values are stored as strings - parse JSON or numbers as needed
- Expiration uses Unix timestamps in seconds (not milliseconds)
- Use
BigInt for expiration and limit values
- Expired keys are automatically deleted and return
null
- Metadata is optional and can store any JSON-serializable data
- List operations support cursor-based pagination for large datasets