Skip to content

Inconsistent behaviors between the native Deno.KV.list method and the one of @deno/kv package #130

@AbdulrhmanGoni

Description

@AbdulrhmanGoni

What's wrong?

I built a desktop client app to manage Deno KV databases.
I designed the app to work with both Deno.KV client and the Node implementation of Deno KV client
provided by the @deno/kv package, since both expose the same API and interact with the same database.

However, while developing and using the app, I noticed some inconsistent behaviors between the native Deno.KV.list() method (in Deno runtime) and the one of the @deno/kv package (in Node).

Behavior Differences

Here are three confusing and unexpected behaviors I encountered:

[1] Selector keys with undefined value

In Deno, when you pass selector keys with undefined value to Deno.KV.list method like the following:

const kv = await Deno.openKv();
const iterator = kv.list({ prefix: ["users"], end: undefined, start: undefined });
for await (const entry of iterator) {/*...*/}

This will work fine as you only specified prefix selector (ignoring end and start because they are undefined)

But when you do the same in Node with @deno/kv package and pass selector keys with undefined value to the kv.list method:

import { openKv } from "@deno/kv";
const kv = await openKv();
const iterator = kv.list({ prefix: ["users"], end: undefined, start: undefined });
for await (const entry of iterator) {/*...*/}

You get the following error:

TypeError: Selector can not specify both 'start' and 'end' keys when specifying 'prefix'

I looked at @deno/kv package's code. Apparently, this happens because the package uses the in operator to check keys existence regardless of the types of their values

[2] Invalid range key with prefix selector

When you pass an invalid range key ("end" or "start") with the prefix selector to Deno.KV.list method like the following:

const kv = await Deno.openKv();
const iterator = kv.list({ prefix: ["users"], end: ["C"] });
for await (const entry of iterator) {/*...*/}

The following error will be thrown (which is expected):

TypeError: End key is not in the keyspace defined by prefix

But passing the same invalid range key in Node to @deno/kv package's list method does not throw any error:

import { openKv } from "@deno/kv";
const kv = await openKv();
const iterator = kv.list({ prefix: ["users"], end: ["C"] });
for await (const entry of iterator) {/*...*/}

No error, even though the end key is outside the keyspace defined by the prefix.

[3] Incomplete selector keys

When passing an incomplete selector (e.g, passing only {start: [...]} or {end: [...]}, or an empty one {}) to the Deno.KV.list method like this:

const kv = await Deno.openKv();
const iterator = kv.list({ start: [10] }); // or `{ end: [10] }` or `{}`
for await (const entry of iterator) {/*...*/}

An error will be thrown (which is also expected):

TypeError: Selector must specify either 'prefix' or both 'start' and 'end' key

But in Node with @deno/kv package, passing an incomplete selector throws a completely different and confusing error:

import { openKv } from "@deno/kv";
const kv = await openKv();
const iterator = kv.list({ start: [10] }); // or `{ end: [10] }` or `{}`
for await (const entry of iterator) {/*...*/}

This error will be thrown:

TypeError: Cannot read properties of undefined (reading 'flatMap')

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions