Write a localStorage wrapper whose entries expire after a given time.
Before you start
What's a TTL cache?
TTL stands for time to live: how long a stored value stays valid, usually given in milliseconds or seconds. A TTL cache gives each entry a TTL when it's stored. Until that much time has passed, reading the entry returns it. From then on the entry has expired, and reading it gives back nothing, as if it had never been stored.
It's for data that goes out of date. An API response, a user's profile, or an auth token can be reused for a while instead of being fetched again every time it's needed, but not forever. The TTL says how old a value may get before you'd rather fetch a fresh one: a minute for a list of products, say.
Here's an entry stored with a TTL of 60 seconds, then read at a few moments:
| Time | What happens | Result |
|---|---|---|
| 0 s | Store user with a TTL of 60 s. | Stored |
| 10 s | Read user. | Found: it's 10 s old. |
| 59.999 s | Read user. | Found: it's 1 ms short of 60 s old. |
| 60 s | Read user. | Not found: it's 60 s old, so it has expired. |
| 75 s | Store user again. | Stored, with a fresh 60 s |
| 100 s | Read user. | Found: the new value is 25 s old. |
A TTL cache and an LRU (least recently used) cache throw entries out for different reasons. An LRU cache goes by use: when it's full, the entry that has gone untouched the longest is evicted, however new it is. A TTL cache goes by age: an entry expires when its time is up, however often it's read, and reading it doesn't make it live any longer. Some caches do both.
TTLs are all over the web. Cache-Control: max-age=3600 on a response tells the browser it's fresh for an hour, so it can be reused without asking the server. In DNS, the system that turns domain names into IP addresses, every record comes with a TTL, in seconds, that says how long it may be cached. A cookie's Max-Age attribute is the number of seconds until it expires. And apps keep API responses for a set time to save requests.
createExpiringStorage(storage, now = Date.now) takes two arguments. storage is any object with the Web Storage methods getItem, setItem, and removeItem, such as localStorage. now is the clock: a function that returns the current time in milliseconds.
It returns an object with three methods. set(key, value, ttlMs) stores any JSON-serializable value, which expires ttlMs milliseconds later; without ttlMs, the entry never expires. remove(key) deletes an entry.
get(key) returns the stored value, or null if the key is missing, expired, or doesn't hold valid JSON. When it finds an expired entry, it removes it from storage.
function createExpiringStorage(storage, now = Date.now) {
// your code
}
// const cache = createExpiringStorage(localStorage);
// cache.set("user", { name: "Ada" }, 60_000); // expires in a minute
// cache.get("user"); // { name: "Ada" }
// // a minute later:
// cache.get("user"); // null, and "user" is removed from localStorage
// cache.set("theme", "dark"); // no ttlMs: never expires
The answer
Write it yourself first. Then compare it with a reference solution and the checks it passes.
Checking your sign-in…