Store Temporary Data
Some data only matters for a while, such as a dismissed banner, a cached
response or a draft. With puter.kv, you can give a key a TTL (time to
live) or an expiry time, and the key is removed when the time is up.
Expire a Value
To have a key delete itself after a while, write it, then call the
puter.kv.expire() method with the number of seconds it should
live:
await puter.kv.set('promoDismissed', true);
await puter.kv.expire('promoDismissed', 60 * 60 * 24); // 24 hours
The seconds are counted on the server, so a wrong clock on the user's device doesn't change when the key expires. A TTL belongs to one key. To have items of a list expire one by one, give each item its own key.
Check Whether It Expired
Once the time passes, puter.kv.get() returns null, the same as
for a key that was never written:
const dismissed = await puter.kv.get('promoDismissed');
if ( ! dismissed ) {
showPromo();
}
From then on, puter.kv.list() leaves the key out too, so you
never filter out stale entries yourself.
Expire at a Set Time
To expire a key at a specific moment instead, such as the end of a sale or
midnight, use the puter.kv.expireAt() method with a Unix
timestamp in seconds:
const midnight = new Date();
midnight.setHours(24, 0, 0, 0);
await puter.kv.expireAt('promoDismissed', Math.floor(midnight.getTime() / 1000));
JavaScript dates count in milliseconds, so divide by 1000. A millisecond value reads as a date tens of thousands of years away, and the key never expires.
The timestamp comes from the user's device, so midnight here is midnight in the user's time zone.
Cache a Response
To cache something you fetch, pass the expiry as the third argument of
puter.kv.set(). It takes the same timestamp in seconds as
puter.kv.expireAt(), and sets the value and the expiry in
one write:
async function getForecast (city) {
const key = `forecast:${ city }`;
const cached = await puter.kv.get(key);
if ( cached ) return cached;
const forecast = await fetchForecast(city);
const inOneHour = Math.floor(Date.now() / 1000) + 60 * 60;
await puter.kv.set(key, forecast, inOneHour);
return forecast;
}
Every call to puter.kv.set() replaces the whole entry, expiry
included. A value you write again and again, such as a draft saved on every
edit, needs the expiry on every write, and each save moves it later:
const inOneWeek = Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7;
await puter.kv.set('draft', text, inOneWeek);
Set a Lock or Cooldown
To run something at most once in a period, such as one reminder every 10
minutes, write a key with an expiry and onlyIfAbsent: true in the options of
puter.kv.set(). The key is written only when it isn't already
there, and the call returns false when it is:
async function claim (key, seconds) {
const expireAt = Math.floor(Date.now() / 1000) + seconds;
return await puter.kv.set(key, true, expireAt, { onlyIfAbsent: true });
}
if ( await claim('cooldown:reminder', 10 * 60) ) {
await sendReminder();
}
Once the key expires, the next claim() call writes it again and returns
true.
To run one sync at a time, use the key as a lock and delete it with
puter.kv.del() when the work is done. If the tab closes in the
middle, the expiry releases the lock:
if ( await claim('lock:sync', 30) ) {
try {
await sync();
} finally {
await puter.kv.del('lock:sync');
}
}
The check and the write are a single call. Two calls at nearly the same moment
from different places can still both get true, so use this where running
twice is harmless.
Remove a TTL
To keep a key for good, write the value again with
puter.kv.set() and no expiry:
await puter.kv.set('draft', text);
To change part of a value and remove its TTL in the same write, pass null as
the TTL of puter.kv.update().
Calls that change part of a value keep the TTL the key already has. That
includes puter.kv.incr(), puter.kv.decr(),
puter.kv.add(), puter.kv.remove(), and
puter.kv.update() without a TTL argument.