Readme

Replacement for Deno KV for Val Town on top of @std/sqlite.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { sqlite } from "https://esm.town/v/std/sqlite?v=6";
import superjson from "npm:superjson";
export class DenoSyntheticKV {
name: string;
created: boolean;
constructor(name: string) {
this.name = name;
}
async open() {
if (this.created) return;
this.created = true;
await sqlite.execute(`CREATE TABLE IF NOT EXISTS ${this.name} (key TEXT PRIMARY KEY, value TEXT)`);
return;
}
async set(key: any, value: any) {
await this.open();
const encodedKey = superjson.stringify(key);
const encodedValue = superjson.stringify(value);
await sqlite.execute({
sql: `INSERT OR REPLACE INTO ${this.name} (key, value) VALUES (?, ?)`,
args: [encodedKey, encodedValue],
});
}
async get<A>(key: any): Promise<{ value: A | null }> {
await this.open();
const encodedKey = superjson.stringify(key);
const response = await sqlite.execute({
sql: `SELECT value FROM ${this.name} WHERE key = ?`,
args: [encodedKey],
});
if (response.rows.length === 0) {
return { value: null };
}
return { value: superjson.parse(response.rows[0][0]) as A };
}
async delete(key: any) {
await this.open();
const encodedKey = superjson.stringify(key);
await sqlite.execute({
sql: `DELETE FROM ${this.name} WHERE key = ?`,
args: [encodedKey],
});
}
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
6
vladimyr avatar

I support the effort but there are a few issues with this implementation:

  1. constructors are always synchronous so you need to keep around the handle to deferred table initialization (https://www.val.town/v/stevekrouse/DenoSyntheticKV#L8)
  2. serializing keys with superjson turns strings into objects (e.g. "foo" becomes {"json":"foo"}) which makes interoperability with existing solutions almost impossible: @vladimyr/kv_example
stevekrouse avatar

Point (1) is an excellent one! I guess that's why they do it as openKV in their implementation

For point (2), I went in this direction because I was more concerned with getting code that uses Deno KV to work (@stevekrouse/passkeys_demo) than I was with anything else, and they store some pretty funky objects in KV storage in that example!

pomdtr avatar

I wonder how hard it would be to create a deno kv backend built on top of std/sqlite. There is one example here: https://github.com/denoland/denokv

vladimyr avatar

You mean translate the actual KV Connect network protocol to a bunch of @std/sqlite API calls?

vladimyr avatar

Well here is the documentation: https://github.com/denoland/denokv/blob/main/proto/kv-connect.md#protocol CRUD stuff requires ProtoBuf while @std/sqlite works with JSON. I'm wondering about the potential performance penalty incurred by marshalling cost 🤔

pomdtr avatar

You mean translate the actual KV Connect network protocol to a bunch of @std/sqlite API calls?

Exactly. But I forgot the whole backend was implemented in rust, this might be trickier than I expected.

v12
April 28, 2024