Skip to content

Custom Cache Driver

The CacheDriver interface is small and simple — implement it to use any storage backend.

Interface

ts
interface CacheDriver {
  get<T>(key: string): Promise<T | undefined>;
  set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
  delete(key: string): Promise<void>;
  clear(): Promise<void>;
}

Vercel KV (Upstash)

ts
import { kv } from "@vercel/kv";
import type { CacheDriver } from "market-feed";

const vercelKVDriver: CacheDriver = {
  async get<T>(key: string) {
    return (await kv.get<T>(key)) ?? undefined;
  },
  async set<T>(key: string, value: T, ttl = 60) {
    await kv.set(key, value, { ex: ttl });
  },
  async delete(key: string) {
    await kv.del(key);
  },
  async clear() {
    await kv.flushdb();
  },
};

Cloudflare KV

ts
// Assumes KV namespace is bound as `MY_KV` in the Worker
import type { CacheDriver } from "market-feed";

function cloudflareKVDriver(kv: KVNamespace): CacheDriver {
  return {
    async get<T>(key: string) {
      const val = await kv.get(key, "json");
      return val as T | undefined;
    },
    async set<T>(key: string, value: T, ttl = 60) {
      await kv.put(key, JSON.stringify(value), { expirationTtl: ttl });
    },
    async delete(key: string) {
      await kv.delete(key);
    },
    async clear() {
      // KV doesn't support bulk delete — list and delete
      const list = await kv.list();
      await Promise.all(list.keys.map(k => kv.delete(k.name)));
    },
  };
}

In-memory with node-cache

ts
import NodeCache from "node-cache";
import type { CacheDriver } from "market-feed";

const nodeCache = new NodeCache();

const nodeCacheDriver: CacheDriver = {
  async get<T>(key: string) {
    return nodeCache.get<T>(key);
  },
  async set<T>(key: string, value: T, ttl = 60) {
    nodeCache.set(key, value, ttl);
  },
  async delete(key: string) {
    nodeCache.del(key);
  },
  async clear() {
    nodeCache.flushAll();
  },
};

Using your driver

ts
import { MarketFeed } from "market-feed";

const feed = new MarketFeed({
  cache: {
    driver: redisDriver,
    // ttlOverrides still work with custom drivers
    ttlOverrides: {
      quote: 30,
      company: 86400,
    },
  },
});

TIP

When a custom driver is provided, maxSize has no effect — the driver manages its own eviction policy.

Released under the MIT License.