メインコンテンツまでスキップ

Key-Valueストア

Zeltは、TTLサポートとatomic操作を備えたnamespaceベースのkey-valueストレージとして @zeltjs/kv を提供します。

概要

KVモジュールは以下を提供します:

  • KVAdaptor / AtomicKVAdaptor — namespace化されたストアを作成するトップレベルのadaptor
  • KVStore / AtomicKVStore — データ操作(get、set、delなど)のためのインターフェース
  • MemoryKV — 自動ガベージコレクションを備えたインメモリ実装
  • Promiseベースのapi — すべての操作は Promise を返し、エラー時にthrowする

インストール

pnpm add @zeltjs/kv

基本的な使い方

MemoryKV をinjectして、namespace化されたストアを作成します:

@Injectable()
export class CacheService {
  private store: AtomicKVStore;

  constructor(private kv = inject(MemoryKV)) {
    this.store = this.kv.namespace('cache');
  }

  async getUser(id: string): Promise<User | undefined> {
    return this.store.get<User>(`user:${id}`);
  }

  async setUser(id: string, user: User): Promise<void> {
    await this.store.set(`user:${id}`, user, { ttlSec: 3600 });
  }
}

KVStoreのメソッド

メソッド説明
get<T>(key)キーで値を取得する
set<T>(key, value, opts?)値をオプションのTTL付きで保存する
del(key)キーを削除する
has(key)キーが存在するか確認する
expire(key, ttlSec)既存のキーのTTLを更新する
namespace(prefix)子namespaceを作成する

TTL(Time-To-Live)

await store.set('session:abc', { userId: '123' }, { ttlSec: 1800 });

// 既存キーのTTLを延長する(セッションのtouchに便利)
await store.expire('session:abc', 1800);

Atomic操作

AtomicKVStoreKVStore を拡張し、atomic操作を追加します:

メソッド説明
incr(key, by?, opts?)atomicなインクリメント(キーがなければ作成する)
setnx<T>(key, value, opts?)キーが存在しない場合のみ設定する

incrによるレート制限

@Injectable()
export class RateLimiter {
  private store: AtomicKVStore;

  constructor(kv = inject(MemoryKV)) {
    this.store = kv.namespace('ratelimit');
  }

  async checkLimit(clientId: string, limit: number): Promise<boolean> {
    const count = await this.store.incr(`req:${clientId}`, 1, { ttlSec: 60 });
    return count <= limit;
  }
}

setnxによる分散ロック

const acquired = await store.setnx('lock:resource', true, { ttlSec: 30 });
if (acquired) {
  // ロックを取得したら作業を行い、その後解放する
  await store.del('lock:resource');
}

Namespace

Namespaceはキーの論理的な分離を提供します。ネストさせることもできます:

const users = kv.namespace('users');
const sessions = kv.namespace('sessions');

const adminSessions = sessions.namespace('admin');

エラーハンドリング

KV操作は失敗時にエラーをthrowします。エラーハンドリングにはtry-catchを使ってください:

try {
  await store.set('key', value, { ttlSec: -1 });
  console.log('Success');
} catch (error) {
  console.error((error as Error).message);
}

エラー種別: INVALID_TTLEMPTY_NAMESPACEINVALID_VALUESTORE_OPERATION_FAILED

MemoryKV

MemoryKV は開発・テスト向けのインメモリ実装です。値をJSONにシリアライズし、60秒ごとにガベージコレクションを実行します。

const app = createApp([http({
    controllers: [AppController],
  })]);