# simple-in-memory-cache

> A simple in-memory cache, for nodejs and the browser, with time based expiration policies.

Latest version **0.5.1** (published 2026-07-23) · MIT license · 0 weekly downloads

## Install

```sh
npm install simple-in-memory-cache
pnpm add simple-in-memory-cache
yarn add simple-in-memory-cache
bun add simple-in-memory-cache
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads; no esm support; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.5.1 |
| Published | 2026-07-23 |
| First published | 2020-09-21 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Node | >=8.0.0 |
| Dependencies | 5 |
| Unpacked size | 31.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 2 |
| Author | ehmpathy |
| Maintainers | uladkasach |
| Keywords | cache, memory, in-memory, browser, browser cache, nodejs, nodejs cache, simple, simple cache, time to live, ttl, expiration |

## Links

- npm: https://www.npmjs.com/package/simple-in-memory-cache
- Repository: https://github.com/ehmpathy/simple-in-memory-cache
- Issues: https://github.com/ehmpathy/simple-in-memory-cache/issues
- npm.io page: https://npm.io/package/simple-in-memory-cache

## Dependencies (5)

- [iso-time](https://npm.io/package/iso-time.md) ^1.11.7
- [type-fns](https://npm.io/package/type-fns.md) 1.21.2
- [uuid-fns](https://npm.io/package/uuid-fns.md) 1.1.3
- [domain-objects](https://npm.io/package/domain-objects.md) 0.31.9
- [helpful-errors](https://npm.io/package/helpful-errors.md) 1.7.3

## Alternatives

- [memory-cache](https://npm.io/package/memory-cache.md) — 795.0K weekly downloads
- [@httptoolkit/proxy-agent](https://npm.io/package/@httptoolkit/proxy-agent.md) — 11.2K weekly downloads
- [express-cache-controller](https://npm.io/package/express-cache-controller.md) — 5.3K weekly downloads
- [http-cache-middleware](https://npm.io/package/http-cache-middleware.md) — 4.5K weekly downloads
- [cache2](https://npm.io/package/cache2.md) — 1.5K weekly downloads

## Recent versions

- 0.5.1 (latest) — 2026-07-23
- 0.4.3 — 2026-06-10
- 0.4.2 — 2026-06-10
- 0.4.0 — 2024-12-26
- 0.3.3 — 2024-09-01
- 0.3.2 — 2024-09-01
- 0.3.1 — 2023-07-28
- 0.3.0 — 2022-11-24
- 0.2.1 — 2022-11-23
- 0.2.0 — 2022-11-23
- 0.1.0 — 2020-09-21

## README

# simple-in-memory-cache

![test](https://github.com/ehmpathy/simple-in-memory-cache/workflows/test/badge.svg)
![publish](https://github.com/ehmpathy/simple-in-memory-cache/workflows/publish/badge.svg)

A simple, typed, in-memory cache for nodejs and the browser with time-based expiration policies.

## install

```sh
npm install --save simple-in-memory-cache
```

## usage

### set and get

```ts
import { createCache } from 'simple-in-memory-cache';

const { set, get } = createCache();
set('purpose of life', 42);
const purpose = get('purpose of life'); // returns 42
```

### expiration

items in the cache expire after 5 minutes by default.

change the default expiration on cache creation:

```ts
const { set, get } = createCache({ expiration: { minutes: 10 } });
```

override expiration per item:

```ts
set('ice cream state', 'solid', { expiration: { seconds: 30 } });
```

set an item to never expire:

```ts
set('speed of light', 299792458, { expiration: null });
```

### invalidation

invalidate a cached item with `set(key, undefined)`:

```ts
set('purpose of life', 42);
get('purpose of life'); // returns 42

set('purpose of life', undefined);
get('purpose of life'); // returns undefined
```

### keys

list all non-expired keys in the cache:

```ts
const { set, keys } = createCache();
set('a', 1);
set('b', 2);
keys(); // returns ['a', 'b']
```

### conditional writes (put-if-absent + compare-and-set)

gate a write on a version precondition, so it succeeds only when the key is in the state you
expect. on a precondition miss the write throws `SimpleCacheConditionError` (never a silent
clobber). this makes the cache usable as a coordination primitive (locks, leases, stampede
control, optimistic concurrency).

read the current opaque version token for a key with `version(key)`:

```ts
import { createCache, SimpleCacheConditionError } from 'simple-in-memory-cache';

const { set, get, version } = createCache<string>();
```

**put-if-absent** — write only if no live entry exists (`condition: { version: null }`):

```ts
set('lock', 'worker-a', { condition: { version: null } }); // ✓ wins — key was open

try {
  set('lock', 'worker-b', { condition: { version: null } }); // ✋ throws — key held
} catch (error) {
  if (!(error instanceof SimpleCacheConditionError)) throw error;
  // worker-b lost the race, loudly — no silent overwrite
}
```

**compare-and-set** — write only if the stored version matches a token from a *prior*
observation. `version()` returns `string | undefined` while a condition wants `string | null`, so
bridge an absent read with `?? null` (absent → put-if-absent, present → compare-and-set):

```ts
set('counter', '1');
const v = version('counter'); // capture the token now

set('counter', '2', { condition: { version: v ?? null } }); // ✓ still at v
// ✋ if someone else wrote 'counter' since you read v, this throws
```

**version-checked get** — read the value only if it is still the version you last saw:

```ts
const v = version('counter');
const current = get('counter', { condition: { version: v ?? null } });
// ✓ returns the value IF it is still at version v
// ✋ throws SimpleCacheConditionError if the version drifted (the value you were about to
//    act on is stale)
```

**compare-and-delete** — release a lock only if it is still yours (`set(key, undefined, …)`):

```ts
set('lock', 'worker-a', { condition: { version: null } });
const mine = version('lock');
set('lock', undefined, { condition: { version: mine ?? null } }); // release, only if still mine
```

> ⚠️ the token must come from a *prior* observation (a `get`/`version`/acquire taken before the
> write). a fresh `version(key)` read taken immediately before its own conditional write always
> matches the current token and so guards no state — the whole point of a condition is to compare
> against a value you saw earlier.

> ⚠️ a **successful conditional write also mints a fresh token**, so the token you used to authorize
> it is immediately dead. in a renew-loop (mutex renewal), re-observe `version(key)` before *each*
> renewal — do not carry the token you acquired at lock time into the second renewal, or it will throw:
>
> ```ts
> set('lock', me, { condition: { version: null } }); // acquire
> let held = version('lock'); // token now
> set('lock', me, { condition: { version: held ?? null } }); // renew #1 → mints a new token
> held = version('lock'); // re-observe before the next renewal
> set('lock', me, { condition: { version: held ?? null } }); // renew #2 ✓ (the old token would throw)
> ```

> ⚠️ **always bridge an absent read with `?? null`.** `version()` yields `string | undefined`, but a
> condition wants `string | null`. typescript callers are safe — a bare `{ version: v }` where
> `v: string | undefined` fails to compile against the condition type. but plain-js/browser callers get
> no such guard: a `{ condition: { version: someUndefinedVar } }` meant as put-if-absent falls into the
> compare-and-set branch and **always throws**, since an `undefined` token never equals the found
> version. use `{ version: v ?? null }` so an absent read means put-if-absent, not a guaranteed miss.

## types

the cache is fully typed:

```ts
import { createCache, SimpleInMemoryCache } from 'simple-in-memory-cache';

const cache: SimpleInMemoryCache<number> = createCache<number>();
cache.set('answer', 42);
const answer: number | undefined = cache.get('answer');
```

## api

### `createCache<T>(options?)`

creates a new cache instance.

**options:**
- `expiration?: IsoDuration | null` — default expiration for items (default: `{ minutes: 5 }`)

**returns:** `SimpleInMemoryCache<T>`

### `SimpleInMemoryCache<T>`

- `get(key, options?): T | undefined` — retrieve an item (returns `undefined` if absent or
  expired). with `options.condition`, verify the stored version before it yields the value; on a
  mismatch throw `SimpleCacheConditionError` (version-checked get).
- `set(key, value, options?): void` — store an item (or invalidate with `value: undefined`). with
  `options.condition`, gate the write: `{ version: null }` = put-if-absent, `{ version: '<token>' }`
  = compare-and-set; on a precondition miss throw `SimpleCacheConditionError`.
- `version(key): string | undefined` — read the current opaque version token for a live key
  (`undefined` if absent or expired). treat the token as equality-only; never parse or order it.
- `keys(): string[]` — list all non-expired keys

**options** (both `get` and `set`):
- `condition?: { version: string | null }` — the version precondition (see conditional writes above)

**options** (`set` only):
- `expiration?: IsoDuration | null` — override the item's expiration (`null` = never expire)

### `SimpleCacheConditionError`

thrown by `get`/`set` when a `condition.version` precondition is not met. extends `ConstraintError`
(from `helpful-errors`) — a caller-must-fix constraint (exit code 2). carries
`{ key, condition, found }` metadata for diagnosis.

---
_Source: https://npm.io/package/simple-in-memory-cache · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
