Skip to content

A simpler approach to storing transient data


At some point, your app will need to store some data. It's unavoidable and it can be a chore. A chore because not every case requires a full blown backend. Every so often, we just need a quick way to persist some data without comitting to a dedicated database at the back - or maybe we just don't want to pollute an existing storage with temporary data that is ephermal in nature and only needed for a short little while.

Demo apps, mock ups, static pages... are all use cases for this service. Well, say hello to PrefsDB!

  • Free, serverless, cloud storage
  • No accounts, no logins required.
  • No databases to create and maintain.
  • No backend: allow your apps to save and retrieve data in NoSQL data storage.
  • Secured with end-to-end encryption, RLS, and isolated API tokens.
  • Data life-span managed with TTL.

The idea is to be able to quickly store and retrieve some data with minimum fuss. With that goal in mind, the API was designed to be as frictionless as possible. The service does not require a login or accounts on your part (however advisable, but more on that later).

User-Pays Model

PrefsDB does snot carry any costs for the developers. As a developer you pay nothing since each user of your app covers their own cloud usage. Whether your app has 1 user or 1 million users, it costs you nothing to run. PrefsDB gives you scalable infrastructure, low barrier to entry, fast deployments completely free.

Each instance of your app will generate an isolated API Key that is unique to your app's user. That token will govern resource usage for that user. Resources allocated to each key are plentiful for a typical usage of an average web app user.

With PrefsDB you can build and distribute applications without worrying about server bills, scaling costs, or surprise usage spikes (and associated spike charges).

Advantages of the User-Pays Model

  1. No server costs. You will never wake up to a surprise cloud bill due to a traffic spike over a weekend.

  2. No accounts, no logins. You can use PrefsDB like a glorified LocalStorage or you can implement a classic login with ease.

  3. Built-in Security. Isolated API Tokens for each instance of your app limits bad actors to their own environment.

  4. Built-In Anti-Abuse. Bad actors have no incentive to abuse the system because they are paying for their own usage.

A better alternative to LocalStorage?


So you're building something and want to let your users save some data without involving a database or a dedicated backend. There are options: your own VPS, cloud solutions, etc... but you're thinking it's all an overkill for this and are seriously considering localStorage.

LocalStorage (an implementation of Web Storage API standard) in JavaScript allows web applications to store data locally within the user's browser – with no expiration date. The data isn’t deleted when the browser is closed, and is available when the browser is opened again.

So far so good. It operates on a simple key-value basis and the data remains persisted even after the user closes the browser or navigates away from the page. The API methods are simple to understand and provide a convenient way to maintain state and store user preferences without relying on server-side storage.

LocalStorage, while often a valid choice for many use cases, it suffers from two fundamental flaws:

  1. Data is lost when user resets the browser (or the device).
  2. Data does not persist across devices. Settings saved on one device cannot be accessed on another.

How does PrefsDB work?


This is where prefsdb shines. It can be used like a typical serverless cloud storage with auth, or it can be utilized in a way similar to localStorage where no auth from the end user is needed (remmeber: each API token is unqiue to the user/instance of the app).

PrefsDB uses on the fly generated API keys (tokens) - on as needed basis - using the credentials/secrets provided by your users: a login handle , a passphrase, "password", anything at all that would make the token unique for your user.

Those credentails never leave the browser, they are never transmitted anywhere, and are never stored anywhere.

Once that step is completed you can store and retrieve data for that particular user.

More practical example: let's say you you built a frontend and now need to preserve some user-specific data: user's preferences, settings, app's state, progress, etc. etc. So you add a credentials collecting form on your page.

The code might look similar to this:

    <input type="text" id="email">
    <input type="password" id="password">

Notice

Notice how we're not using <form> tag. That's because we will not be submitting a form. We're also not asking the user to "create an account" first. We simply create an API token using the supplied credentials and store data.

What's next? Well, under normal circumstances, now wyou'd need to spend time building and provisioning a backend to store that data, right? You may already have something in place, and that's great. But if you don't, PrefsDB can come in handy.

A simple call to PrefsDB API can save data for your user.

    /* retrieve user input */
    let email = document.getElementById("email").value;
    let pass  = document.getElementById("password").value;

    /* store data once key is generated */
    prefsdb.getkey("project_name", [email, pass, salt], {
        onsuccess: (response) => {
            prefsdb.write(data);
        }
    });

Tip

Successfull call to getkey() or createkey() initializes your prefsdb instance with the appropriate API security tokens.

And that's it. This is the most basic use case for PrefsDB. The content of "email" and "password" variables is never stored anywhere, never leaves the client (PrefsdDB hashes all values). That data can be retrieved with corresponding .read() call.

    prefsdb.getkey("project_name", [email, pass, salt], {
        onsuccess: (response) => {
            prefsdb.read({ onsuccess: (result)=>{
                data = result.value;
            } });
        }
    });
Same as above but using Promises:
    prefsdb.getkey("project_name", [email, pass, salt])
        .then( (response) => response.json() )
        .then( (response) => {
            prefsdb.read()
                .then((response)=>response.json())
                .then((response)=>{ data = response.value; });
        });

Why PrefsDB is a better localStorage alternative?


Yes, local storage in the web browser is very convenient and easily accessible. However, it suffers from a few major shortcomings:

  1. Local storage does not survive data wipe or device reset. When users clear the browser cache they will loose the data.

  2. Local storage is not secure: data stored in plain text. Susceptible to injection attacks.

  3. Rather low limit of 5 MB of storage.

  4. No device switching. Data saved with LocalStorage on one device cannot be retrieved on another.

  5. LocalStorage offers synchronous operation only. Each read or write operation will be performed seqientially - one-at-a-time. Prefsdb.com API was designed from the ground up to be asynchronous.

  6. LocalStorage can't be used by web workers. If you build an application that does background processing, localstorage may be out of reach.

  7. Incognito mode issues. Incognito or "private" browsing tabs will not retain local or session storage.

PrefsDB API addresses all of the above shortcomings with a simple, clean, and secure API.

With PrefsDB:

  1. Your app's data will survive a device reset.

  2. Data can be restored on any other device.

  3. No 5 MB total storage limits.

  4. User's data is protected with isolated API keys, built-in RLS, and end-to-end encryption. You can always store self-encrypted data.

  5. Works in private/incognito mode across browsers and tabs.

Data security


The API utilizes Password-Based Encryption process to generate a unique API token which will govern the use of your or your users' data. Any credentials supplied by your users or secrets supplied by your app are contained within the scope of the client. They never leave the browser.

Data-in-motion.

All communication and traffic between the client (your app) and the server server is secured with industry standard HTTPS protocol. Even though your app may not use http (not recommended), and and all communication with the API is over HTTPS.

Data-at-rest.

All data is stored encrypted secured by AES-256 encryption algorythm.

API token isolation

Token-gated architecture seperates token space and any and all API keys (tokens) are bounded to their origin.

Row-Level-Security.

Your individual customer's data is gated by the boundaries set by the unique API token isolated to each instance of your app.

Data collection types


As of now there are two types of data that can be managed with prefsdb.com:

  1. Key/value pairs and

  2. Lists.

Key-Value pairs

Key/value pairs are fairly self-explanatory, each "key" may be assigned a "value". Duplicate assignments will result in a value (for a given key) be overwritten.

.key("user").write('{"score":"200", "level":"3" }');

Lists and collections

Lists are unordered collections of values. We add and remove items to and from a list by specifying a list's name and a value we want added (or removed).

.list("signups").add("jane.doe@somewhere.com");

Retrieve all items in a list

// uses a callback 
.list("signups")
        .read( {
            onsuccess: (R) => {
                console.log(data.values);
            }
        });

Organizing and grouping data

Each API token can organize and store your data under any number of "projects", "domains", or "subdomains". There is no inherent meaning behind those terms, they are just grouping keywords for organizing your data. For instance "domain" may refer to an internet domain or a knowledge base domain.

var gameJson = {...};

.domain("ultimafaux")
        .key("user")
        .write(gameJson);

Callbacks and Promises

Retrieve data using callback functions or handle Promise directly, whatever fits your style.

// using .read method a callback 
.domain("ultimafaux")
        .key("user")
        .read({
            onsuccess: (data) => {
                gameJson = data.value;
            }
        });
Same as above but with a Promise
// using .read method with a Promise
.key("user").read()
    .then( (response) => response.json())
    .then( (data) => {
        console.log(data.values);
    })
    .catch( (error) => {
        console.log(error.name);
    });

Time-To-Live


You can set an arbirary time-to-live constraint on any piece of data. TTL values are expressed in "days". Inactive data will be purged from the system when TTL has been reached. Inactive means it was not read nor written to (updated) within TTL time window.

var stuff = {...};

// store game state for 90 days
.domain("demostuff")
        .project("myclient")
        .ttl(10)
        .key("proposal123")
        .write(stuff);
The above entry will be automatically purged from the system after 10 days of inactivity (no reads or writes).