Last active
September 29, 2023 14:13
-
-
Save NuroDev/15e1f8f9a5f8050c8717e872778ea79a to your computer and use it in GitHub Desktop.
π Create typed KV β A wrapper for a Deno KV instance to provide a more ORM style API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Prettify<T> = | |
& { [K in keyof T]: T[K] } | |
& {}; | |
type KvConsistencyOptions = Parameters<Deno.Kv['get']>[1]; | |
interface MapTypedKv<K extends string, V extends unknown> { | |
delete(id: K): Promise<void>; | |
get(id: K, options?: KvConsistencyOptions): Promise<Deno.KvEntryMaybe<V>>; | |
getMany<T extends readonly unknown[]>( | |
keys: readonly [...{ [L in keyof T]: K }], | |
options?: KvConsistencyOptions, | |
): Promise<{ [L in keyof T]: Deno.KvEntryMaybe<V> }>; | |
list(options?: Deno.KvListOptions): Promise<Deno.KvListIterator<V>>; | |
set(id: K, value: Partial<V>): Promise<void>; | |
} | |
/** | |
* Creates a typed wrapper around a Deno.Kv instance. | |
* | |
* @example | |
* ```ts | |
* interface Post { | |
* title: string; | |
* slug: string; | |
* content: string; | |
* } | |
* | |
* interface User { | |
* name: string; | |
* email: string; | |
* } | |
* | |
* type Database = { | |
* posts: [`pst_${number}`, Post]; | |
* users: [`usr_${string}`, User]; | |
* }; | |
* | |
* const db = createdTypedKv<Database>(kv); | |
* | |
* const user = await db.users.get('usr_abc123'); | |
* // ^? Deno.KvEntryMaybe<User> | |
* | |
* const posts = await db.posts.getMany(['pst_1', 'pst_2', 'pst_3']); | |
* // ^? [Deno.KvEntryMaybe<Post>, Deno.KvEntryMaybe<Post>, Deno.KvEntryMaybe<Post>] | |
* ``` | |
* | |
* @param kv - The Deno.Kv instance to wrap. | |
* | |
* @returns - A typed wrapper around the Deno.Kv instance. | |
*/ | |
export function createdTypedKv< | |
TDatabase extends Record<string, [key: string, value: unknown]>, | |
>(kv: Deno.Kv) { | |
return new Proxy(kv, { | |
get: (_, prop: string) => ({ | |
delete: (id: string) => kv.delete([prop, id]), | |
get: (id: string, options?: KvConsistencyOptions) => | |
kv.get([prop, id], options), | |
getMany: <T extends readonly unknown[]>( | |
keys: readonly [...{ [K in keyof T]: string }], | |
options?: KvConsistencyOptions, | |
) => kv.getMany(keys.map((key) => [prop, key] as const), options), | |
list: (options?: Deno.KvListOptions) => | |
kv.list({ prefix: [prop] }, options), | |
set: (id: string, value: Partial<unknown>) => | |
kv.set([prop, id], value), | |
}), | |
}) as { | |
[K in keyof TDatabase]: Prettify< | |
MapTypedKv<TDatabase[K][0], TDatabase[K][1]> | |
>; | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface User { | |
name: string; | |
email: string; | |
} | |
interface Post { | |
title: string; | |
slug: string; | |
content: string; | |
} | |
type Database = { | |
posts: [`pst_${number}`, Post]; | |
users: [`usr_${string}`, User]; | |
}; | |
const db = createdTypedKv<Database>(kv); | |
await db.users.set('usr_abc123', { | |
name: 'Tim Apple', | |
email: '[email protected]', | |
}); | |
const user = await db.users.get('usr_abc123'); | |
// ^? Deno.KvEntryMaybe<User> | |
await db.users.delete('usr_abc123'); | |
const posts = await db.posts.getMany(['pst_1', 'pst_2']); | |
// ^? [Deno.KvEntryMaybe<Post>, Deno.KvEntryMaybe<Post>] | |
const list = await db.posts.list(); | |
// ^? Deno.KvListIterator<Post> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment