web-kv
v1.0.7
Published
Modern key-value storage for the web with IndexedDB and localStorage support
Maintainers
Readme
web-kv
A modern, performant key-value storage library designed for web applications. Built with TypeScript and optimized for both development experience and production performance.
Table of Contents
Installation
npm install web-kvyarn add web-kvbun add web-kvQuick Start
import kv from 'web-kv';
// Store a value
await kv.set('user', { name: 'John', email: '[email protected]' });
// Retrieve a value
const user = await kv.get('user');
// Check existence
const exists = await kv.has('user');
// Delete a value
await kv.delete('user');Core Concepts
Storage Engines
web-kv automatically selects the optimal storage engine for your environment:
| Engine | Storage Limit | Performance | Use Case | |--------|---------------|-------------|----------| | IndexedDB | ~50% of disk | Excellent | Primary storage, large datasets | | localStorage | ~5-10 MB | Good | Fallback, legacy browsers |
The library defaults to IndexedDB when available, with automatic fallback to localStorage.
Namespaces
Isolate data using namespaces to prevent key collisions:
const userStore = await kv.create({ namespace: 'users' });
const cacheStore = await kv.create({ namespace: 'cache' });
// These are completely isolated
await userStore.set('data', { type: 'user' });
await cacheStore.set('data', { type: 'cache' });Time-To-Live (TTL)
Automatically expire entries after a specified duration:
// Expire after 1 hour (3600 seconds)
await kv.set('session', token, { ttl: 3600 });
// Check remaining time
const ttlInfo = await kv.ttl('session');
console.log(`Expires in ${ttlInfo?.remaining} seconds`);
// Extend TTL
await kv.expire('session', 7200); // Extend to 2 hours
// Remove TTL (make permanent)
await kv.persist('session');LRU Eviction
Prevent unbounded storage growth with Least Recently Used eviction:
const cache = await kv.create({
namespace: 'api-cache',
maxItems: 1000, // Maximum 1000 entries
maxSize: 10485760, // Maximum 10 MB
lru: true, // Enable LRU eviction
});When limits are exceeded, the least recently accessed items are automatically removed.
API Reference
Basic Operations
get<T>(key: string): Promise<T | undefined>
Retrieve a value by key.
const user = await kv.get<User>('user:123');set<T>(key: string, value: T, options?): Promise<void>
Store a value with optional TTL and priority.
await kv.set('key', value, {
ttl: 3600, // Expires in 1 hour
priority: 8, // Higher priority (1-10, higher = kept longer)
});delete(key: string): Promise<boolean>
Remove a key. Returns true if the key existed.
const wasDeleted = await kv.delete('key');has(key: string): Promise<boolean>
Check if a key exists.
if (await kv.has('key')) {
// Key exists
}Batch Operations
Perform multiple operations efficiently in a single transaction.
getMany<T>(keys: string[]): Promise<Map<string, T | undefined>>
const results = await kv.getMany(['key1', 'key2', 'key3']);setMany<T>(entries: Entry<T>[], options?): Promise<void>
await kv.setMany([
{ key: 'a', value: 1 },
{ key: 'b', value: 2 },
{ key: 'c', value: 3, options: { ttl: 60 } },
]);deleteMany(keys: string[]): Promise<Map<string, boolean>>
const results = await kv.deleteMany(['key1', 'key2']);Query Operations
keys(options?): Promise<string[]>
Get all keys in the current namespace.
const allKeys = await kv.keys();
const limitedKeys = await kv.keys({ limit: 10, offset: 0 });values<T>(options?): Promise<T[]>
Get all values.
const allValues = await kv.values<User>();entries<T>(options?): Promise<Entry<T>[]>
Get all key-value pairs.
const entries = await kv.entries<User>();
entries.forEach(({ key, value }) => {
console.log(key, value);
});Search & Filtering
prefix<T>(prefix: string, options?): Promise<Entry<T>[]>
Find entries by key prefix.
const userEntries = await kv.prefix('user:');suffix<T>(suffix: string, options?): Promise<Entry<T>[]>
Find entries by key suffix.
const jsonEntries = await kv.suffix('.json');search<T>(pattern: string | RegExp, options?): Promise<Entry<T>[]>
Search using regular expressions.
const matches = await kv.search(/^cache:api:.*/);
const caseInsensitive = await kv.search('user', { caseInsensitive: true });Pagination
list<T>(options): Promise<PaginatedResult<T>>
Paginate through large datasets.
const page1 = await kv.list({
limit: 20,
offset: 0,
sortBy: 'createdAt',
order: 'desc',
});
console.log(`Total: ${page1.total}`);
console.log(`Has more: ${page1.hasMore}`);Iteration
forEach<T>(callback, options?): Promise<void>
Iterate over all entries.
await kv.forEach((value, key, metadata) => {
console.log(key, value, metadata.createdAt);
// Return false to stop iteration
if (key === 'stop') return false;
});Utility Methods
| Method | Description |
|--------|-------------|
| count(options?) | Count entries in the namespace |
| size() | Get total storage size in bytes |
| clear() | Remove all entries in the namespace |
| stats() | Get storage statistics |
| ttl(key) | Get TTL info for a key |
| expire(key, ttl) | Set/update TTL for a key |
| persist(key) | Remove TTL from a key |
| create(options?) | Create new instance with custom config |
Advanced Usage
Encryption
Protect sensitive data with AES-256-GCM encryption:
const secureStore = await kv.create({
namespace: 'sensitive',
encryption: {
enabled: true,
key: 'your-strong-password',
},
});
// Data is encrypted at rest
await secureStore.set('api-key', 'sk-secret-key-here');Events
Subscribe to storage events:
const store = await kv.create();
store.on('set', (key, value, metadata) => {
console.log(`Set: ${key}`);
});
store.on('delete', (key, success) => {
console.log(`Deleted: ${key}`);
});
store.on('expired', (key) => {
console.log(`Expired: ${key}`);
});
store.on('evicted', (key, reason) => {
console.log(`Evicted: ${key} due to ${reason}`);
});
store.on('error', (error) => {
console.error('Storage error:', error);
});Supported Data Types
All JavaScript types are fully supported with automatic serialization:
| Type | Example |
|------|---------|
| Primitives | string, number, boolean, null, undefined |
| Objects | { nested: { data: true } } |
| Arrays | [1, 2, 3], [[nested]] |
| Date | new Date() |
| RegExp | /pattern/gi |
| Map | new Map([['key', 'value']]) |
| Set | new Set([1, 2, 3]) |
| BigInt | BigInt(9007199254740991) |
| TypedArrays | Uint8Array, Float32Array, etc. |
| Error | new Error('message') |
Configuration
Instance Options
When creating a new instance with kv.create(options), the following options are available:
Storage Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| namespace | string | "default" | Unique identifier to isolate this instance's data from others. Use different namespaces to separate data between features or modules. |
| engine | "indexeddb" | "localstorage" | "auto" | "auto" | Storage backend to use. "auto" prefers IndexedDB with localStorage fallback. |
Capacity Limits
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| maxItems | number | 0 | Maximum number of entries allowed. Set to 0 for unlimited. When exceeded, LRU eviction removes oldest items. |
| maxSize | number | 0 | Maximum total storage size in bytes. Set to 0 for unlimited. When exceeded, LRU eviction removes oldest items. |
| lru | boolean | false | Enable Least Recently Used eviction. When true, oldest accessed items are removed when limits are exceeded. |
| priority | number | 5 | Default priority for new entries (1-10). Higher priority items are kept longer during LRU eviction. |
Time-To-Live
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| ttl | number | 0 | Default expiration time in seconds for new entries. Set to 0 for no expiration. Can be overridden per-entry. |
| autoCleanup | boolean | true | Automatically remove expired entries in the background. Disable for manual cleanup control. |
| cleanupInterval | number | 60000 | Interval in milliseconds between automatic cleanup runs. Lower values = more frequent cleanup. |
Encryption
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| encryption.enabled | boolean | false | Enable AES-256-GCM encryption for all stored values. |
| encryption.key | string | - | Password for encryption. Required when enabled is true. Use a strong, unique password. |
Performance
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| batchSize | number | 100 | Number of items to process in a single batch during bulk operations. Larger = faster but more memory. |
Set Options
When storing values with kv.set(key, value, options):
| Option | Type | Description |
|--------|------|-------------|
| ttl | number | Time-to-live in seconds. Overrides default ttl for this entry. |
| priority | number | Priority level 1-10. Overrides default priority for this entry. |
| metadata | object | Custom metadata to store alongside the value. |
Query Options
When querying with keys(), values(), entries(), count():
| Option | Type | Description |
|--------|------|-------------|
| prefix | string | Filter keys starting with this string. |
| suffix | string | Filter keys ending with this string. |
| limit | number | Maximum number of results to return. |
| offset | number | Number of results to skip (for pagination). |
| includeExpired | boolean | Include expired entries in results (default: false). |
Tree Shaking
This package is fully tree-shakable. Only import what you need:
// Full default instance
import kv from 'web-kv';
// Named exports for advanced usage
import { KV, EventEmitter, serialize, deserialize } from 'web-kv';
// Only type imports (zero runtime cost)
import type { KVOptions, Entry, StorageStats } from 'web-kv';Browser Support
| Browser | Minimum Version | |---------|-----------------| | Chrome | 60+ | | Firefox | 60+ | | Safari | 11+ | | Edge | 79+ |
