Skip to content

Instantly share code, notes, and snippets.

@estrattonbailey
Created April 27, 2023 17:53
Show Gist options
  • Save estrattonbailey/958c6b5f4b62b6c6d7862b7bc495b455 to your computer and use it in GitHub Desktop.
Save estrattonbailey/958c6b5f4b62b6c6d7862b7bc495b455 to your computer and use it in GitHub Desktop.
Cloudflare KV Cache Wrapper (WIP)
type CacheResponse<T> = {
data: T;
stale: boolean;
};
type CacheEnvelope<T> = {
data: T;
options: {
staleAt?: number;
};
};
/**
* Generic storage class. DO NOT use this directly. Instead, use the exported
* storage instances below.
*/
export class Cache<Schema extends Record<string, unknown>> {
protected store: KVNamespace;
constructor({ kv }: { kv: KVNamespace }) {
this.store = kv;
}
/**
* Store a value in storage based on scopes and/or keys
*
* `set([key], value)`
* `set([scope, key], value)`
*/
async set<Key extends keyof Schema>(
key: Key,
data: Schema[Key],
options: { staleTime?: number } = {}
) {
const envelope: CacheEnvelope<Schema[Key]> = {
data,
options: {
staleAt: options.staleTime ? Date.now() + options.staleTime : undefined,
},
};
await this.store.put(String(key), JSON.stringify(envelope));
}
/**
* Get a value from storage based on scopes and/or keys
*
* `get([key])`
* `get([scope, key])`
*/
async get<Key extends keyof Schema>(
key: Key
): Promise<CacheResponse<Schema[Key]> | null> {
const res = await this.store.get(String(key));
if (!res) return null;
// parsed from storage structure `{ data: <value> }`
const { data, options } = JSON.parse(res) as CacheEnvelope<Schema[Key]>;
return {
data,
stale: options.staleAt ? Date.now() > options.staleAt : false,
};
}
/**
* Remove a value from storage based on scopes and/or keys
*
* `remove([key])`
* `remove([scope, key])`
*/
async remove<Key extends keyof Schema>(key: Key) {
await this.store.delete(String(key));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment