Browser extension storage "get" return value if nothing found

Hello, according to: Browser storage get on MDN - the return value is not “null” or “empty” depending on the request parameters for the storage.

I am working on a extension which heavily relies on the storage data. To ease development, I wrote a small helper get/set function, here only the “get” function of the extension (Flag Cookies “get Storage” function on Github).

The real issue with the current “get” function in the browser extension, at least in my case/understanding, there is no “null” or “empty” or something along that line. I would expect, if nothing is found, nothing will be returned by the “get call”. Simple as that. :slight_smile:

Or is there another way to use the “promise” based approach but return nothing to a calling “real time” function in the extension? Perhaps when using “await” and a “async function” call?

Any help would be appreciated!

To distinguish between cases “key-value pair not found” and “key found, but value is set to null/undefined”, they created API that always returns an object - with, or without the key/value pair.

And your code is already checking that: if (Object.keys(value).length === 0) {

Additionally, by returning an object, you can return more than one key-value pair!
Which is used when you ask for multiple values (you can pass an array of strings and you will get object with multiple keys/values).

Note that you can also use destructing:

const {hello} = await browser.storage.local.get('hello');
// hello will be undefined

And you can also use default values:

const {hello = 'hi'} = await browser.storage.local.get('hello');
// hello will be set to 'hi'

AND you can even rename it if you don’t like it:

const {hello: hi = 'hi'} = await browser.storage.local.get('hello');
// hi will be set to 'hi'

:open_mouth:

NOTE: Firefox supports also Chrome namespace, so you can just use “chrome” everywhere. Although, it’s likely better to use “browser” everywhere and use webextension-polyfill in Chrome.

Sorry for the delayed response.

And here is the exact problem, if “no matching element is defined” the API always, after testing your examples, returns something but nothing what is desired. From what I understand at present there is simply no “false/empty/null” or what so ever value returned.

Setting a default for the variable does not work out (as I tried to do) and always returns at least the request keys.

I do not exactly understand why returning nothing is such a difficult thing.

As a suggestion on what I would do:
Provide the “get” storage API function a additional and optional parameter which is returned if nothing matches the keys.

This would make sense in such a case that it would be easy possible to have a default if really needed and let it be any value required. Either a bool, a string or another object or whatever.

If you request a single key and that key is not found, the get() call will return an empty object. In JavaScript, you can distinguish between an undefined property and a defined property with the value undefined. There are a few different ways to do this, but I’d suggest using Object.hasOwn.

let data;
// This storage area is empty
data = await browser.storage.local.get(null);
console.log("data is", data);                   // data is {}

// Check for a known key that has NOT been set
data = await browser.storage.local.get("greeting");
console.log("data is", data);                   // data is {}
console.log("Is 'greeting' set?");              // Is 'greeting' set?
console.log(Object.hasOwn(data, "greeting"));   // false

// Check for a known key that HAS been set
await browser.storage.local.set({greeting: "Hello, world!"});
data = await browser.storage.local.get("greeting");
console.log("data is", data);                   // data is {greeting: "Hello, world!"}
console.log("Is 'greeting' set?");              // Is 'greeting' set?
console.log(Object.hasOwn(data, "greeting"));   // true

The API supports something similar to what you’ve suggested, but more granular. By calling the get() method with an object, you can set the default value for each key that doesn’t have a value in the storage area.

data = await browser.storage.local.get({
  "greeting": "Hello!",
  "username": "DEFAULT"
});

// Default value is NOT used (value has already been set)
console.log("greeting", data.greeting); // Hello, world!

// Default value IS used (value has not been set)
console.log("username", data.username); // DEFAULT

Thank you for your suggestions and help.

There is one issue which I am uncertain about, if the “request” (Object) is exactly the same as what is “delivered”, there is no way to test if the “value” was retrieved from the store or if it is just a simple return in case the value was not found.

According to: get - Return value

[…] If keys is an object, keys that are not found in the storage area will have their values given by the keys object.

The point, if “keys” is an object the value provided by the “get call” is returned, “if not found”.

  • I have always a “Object get request”.

This means, if the request is the same as what “might be” expected the “get call” will always return something, even so the value is not present in the storage.

For example this code parts:

grafik

In both parts the request object is populated with the input of the function.

But how do I apply a sane solution to something like this?
request = { [keyStore]: { [key]: targetValue } };
request = { [keyStore]: key };

If the values are present, it should exactly return the values reqeuested. “But” if not present it exactly returns the same “object” as stated in the (get - Return value) link above.

Do I miss anything here?