Skip to content

Commit c9f0edd

Browse files
committed
feat: add bulk gets for kv in miniflare
1 parent 409825b commit c9f0edd

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

packages/miniflare/src/workers/kv/namespace.worker.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import {
33
DeferredPromise,
44
DELETE,
55
GET,
6+
POST,
67
HttpError,
78
KeyValueStorage,
89
maybeApply,
910
MiniflareDurableObject,
1011
PUT,
1112
RouteHandler,
13+
KeyValueEntry,
1214
} from "miniflare:shared";
1315
import { KVHeaders, KVLimits, KVParams } from "./constants";
1416
import {
@@ -73,6 +75,31 @@ function secondsToMillis(seconds: number): number {
7375
return seconds * 1000;
7476
}
7577

78+
async function processKeyValue(obj: KeyValueEntry<unknown> | null, type: string = "text", withMetadata: boolean = false) {
79+
const decoder = new TextDecoder();
80+
let r = "";
81+
if(obj?.value) {
82+
for await (const chunk of obj?.value) {
83+
r += decoder.decode(chunk, { stream: true });
84+
}
85+
r += decoder.decode();
86+
}
87+
88+
let val = null;
89+
try {
90+
val = obj?.value == null ? null : type === "json" ? JSON.parse(r) : r;
91+
} catch (err: any) {
92+
throw new HttpError(400, "At least of of the requested keys corresponds to a non-JSON value");
93+
}
94+
if (val == null) {
95+
return null;
96+
}
97+
if (withMetadata) {
98+
return { value: val, metadata: obj?.metadata ? JSON.stringify(obj?.metadata) : null };
99+
}
100+
return val;
101+
}
102+
76103
export class KVNamespaceObject extends MiniflareDurableObject {
77104
#storage?: KeyValueStorage;
78105
get storage() {
@@ -81,12 +108,31 @@ export class KVNamespaceObject extends MiniflareDurableObject {
81108
}
82109

83110
@GET("/:key")
111+
@POST("/bulk/get")
84112
get: RouteHandler<KVParams> = async (req, params, url) => {
85113
// Decode URL parameters
86114
const key = decodeKey(params, url.searchParams);
87115
const cacheTtlParam = url.searchParams.get(KVParams.CACHE_TTL);
88116
const cacheTtl =
89117
cacheTtlParam === null ? undefined : parseInt(cacheTtlParam);
118+
if(req.body != null) { // get bulk
119+
// get bulk
120+
let r = "";
121+
const decoder = new TextDecoder();
122+
for await (const chunk of req.body) {
123+
r += decoder.decode(chunk, { stream: true });
124+
}
125+
r += decoder.decode();
126+
const parsedBody = JSON.parse(r);
127+
const keys: string[] = parsedBody.keys;
128+
const obj: {[key: string]: any} = {};
129+
for(const key of keys) {
130+
validateGetOptions(key, { cacheTtl: parsedBody?.cacheTtl });
131+
const entry = await this.storage.get(key);
132+
obj[key] = await processKeyValue(entry, parsedBody?.type, parsedBody?.withMetadata);
133+
}
134+
return new Response(JSON.stringify(obj));
135+
}
90136

91137
// Get value from storage
92138
validateGetOptions(key, { cacheTtl });

0 commit comments

Comments
 (0)