{"id":51544990,"url":"https://github.com/robertolima-dev/rust-node-cache","last_synced_at":"2026-07-09T17:01:56.311Z","repository":{"id":367138340,"uuid":"1279363273","full_name":"robertolima-dev/rust-node-cache","owner":"robertolima-dev","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-24T17:21:11.000Z","size":68,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T19:09:53.904Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertolima-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-24T16:05:56.000Z","updated_at":"2026-06-24T17:21:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/robertolima-dev/rust-node-cache","commit_stats":null,"previous_names":["robertolima-dev/rust-node-cache"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/robertolima-dev/rust-node-cache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-node-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-node-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-node-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-node-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertolima-dev","download_url":"https://codeload.github.com/robertolima-dev/rust-node-cache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-node-cache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35306717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-09T17:01:54.345Z","updated_at":"2026-07-09T17:01:56.313Z","avatar_url":"https://github.com/robertolima-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rust-node-cache\n\n**Ultra-fast in-memory cache powered by Rust.**\n\n`rust-node-cache` is a high-performance in-memory cache for Node.js applications\nwith a core written in Rust. It is built on top of a concurrent, lock-sharded\nhash map ([DashMap](https://docs.rs/dashmap)), giving you fast reads, fast\nwrites, and thread-safe access with very low overhead.\n\nIt ships as a prebuilt native addon (via [napi-rs](https://napi.rs/)), so there\nis **no compiler required at install time** on supported platforms.\n\n[![CI](https://github.com/robertolima-dev/rust-node-cache/actions/workflows/CI.yml/badge.svg)](https://github.com/robertolima-dev/rust-node-cache/actions/workflows/CI.yml)\n[![npm](https://img.shields.io/npm/v/rust-node-cache.svg)](https://www.npmjs.com/package/rust-node-cache)\n![node](https://img.shields.io/badge/node-%3E%3D18-43853d)\n![license](https://img.shields.io/badge/license-MIT-blue)\n\n🌐 **Website:** [rust-node-cache.vercel.app](https://rust-node-cache.vercel.app/)\n\n---\n\n## Features\n\n- 🦀 **Rust-powered** core for predictable, low-overhead performance\n- 🔒 **Thread-safe** via `DashMap` (lock-sharded concurrent map)\n- ⏱️ **TTL support** per entry (lazy expiration + active cleanup)\n- 🧹 **Background expiration** — optional sweeper thread (`cleanupIntervalSeconds`)\n- 🧠 **LRU eviction** — bound the cache by `maxSize` with `evictionPolicy: \"lru\"`\n- ⚡ **Fast reads** and **fast writes**\n- 📊 Built-in **statistics** (hits, misses, sets, deletes, expired, evicted, size)\n- 🧩 **TypeScript** support with generics out of the box\n- 🌐 **Framework agnostic** — works with Express, Fastify, NestJS, Hono,\n  Next.js, or plain Node.js\n\n---\n\n## Installation\n\n```bash\nnpm install rust-node-cache\n```\n\nWorks with both **ESM** and **CommonJS**, and requires **Node.js 18+**.\n\n---\n\n## Quick Start\n\n```ts\nimport { Cache } from \"rust-node-cache\";\n\nconst cache = new Cache();\n\ncache.set(\"user:1\", { id: 1, name: \"Roberto\" });\n\nconst user = cache.get\u003c{ id: number; name: string }\u003e(\"user:1\");\nconsole.log(user); // { id: 1, name: \"Roberto\" }\n```\n\n```js\n// CommonJS\nconst { Cache } = require(\"rust-node-cache\");\n\nconst cache = new Cache();\ncache.set(\"greeting\", \"hello\");\nconsole.log(cache.get(\"greeting\")); // \"hello\"\n```\n\n---\n\n## Basic Usage\n\n### `set(key, value, options?)`\n\nStores (or overwrites) a value. Returns `true` on success, or `false` when a\n`maxSize` limit is set, the cache is full, and the key is new.\n\n```ts\ncache.set(\"user:1\", { id: 1, name: \"Roberto\" });\ncache.set(\"session:123\", session, { ttlSeconds: 60 });\n```\n\n### `get(key)`\n\nReturns the stored value, or `null` if the key is missing or expired.\n\n```ts\nconst user = cache.get\u003cUser\u003e(\"user:1\"); // User | null\n```\n\n### `delete(key)`\n\nRemoves a key. Returns `true` if it existed, `false` otherwise.\n\n```ts\ncache.delete(\"user:1\"); // true\n```\n\n### `exists(key)`\n\nReturns `true` if the key exists and is still valid (not expired).\n\n```ts\ncache.exists(\"user:1\"); // boolean\n```\n\n### `clear()`\n\nRemoves every entry from the cache.\n\n```ts\ncache.clear();\n```\n\n### `size()`\n\nReturns the number of keys currently stored.\n\n```ts\ncache.size(); // 42\n```\n\n---\n\n## TTL\n\nSet an entry with an expiration in seconds:\n\n```ts\ncache.set(\"session:123\", session, { ttlSeconds: 60 });\n```\n\nAfter the TTL elapses, reads return `null`:\n\n```ts\ncache.get(\"session:123\"); // null (once expired)\n```\n\nExpiration is **lazy** by default: an expired entry is evicted the moment it is\naccessed. To proactively reclaim memory from keys that are never read again, run\nan active sweep:\n\n```ts\nconst removed = cache.cleanupExpired(); // number of entries removed\n```\n\nOr let the cache sweep for you in the background — set `cleanupIntervalSeconds`\nand a thread reclaims expired entries on its own (no manual `cleanupExpired()`):\n\n```ts\nconst cache = new Cache({ cleanupIntervalSeconds: 30 });\n```\n\nThe thread is tied to the cache's lifetime: it stops automatically when the cache\nis garbage-collected.\n\n---\n\n## Eviction (maxSize)\n\nCap the number of keys with `maxSize`. When the cache is full and a **new** key\nis written, `evictionPolicy` decides what happens:\n\n```ts\n// Default: reject the write (set returns false), keep existing entries.\nconst a = new Cache({ maxSize: 1000 }); // evictionPolicy: \"reject\"\n\n// LRU: evict the least-recently-used entry to make room, then insert.\nconst b = new Cache({ maxSize: 1000, evictionPolicy: \"lru\" });\nb.evictionPolicy; // \"lru\"\n```\n\nLRU recency is tracked per entry (updated on every `get` hit) and the evicted\ncount is surfaced in `stats().evicted`. Overwriting an existing key never evicts.\n\n---\n\n## Statistics\n\n```ts\ncache.stats();\n```\n\nReturns:\n\n```ts\n{\n  hits: 1500,\n  misses: 200,\n  sets: 800,\n  deletes: 20,\n  expired: 15,\n  evicted: 40,\n  size: 780\n}\n```\n\nCounters are cumulative for the lifetime of the cache instance. `clear()` empties\nthe storage but does **not** reset the historical counters.\n\n---\n\n## Performance\n\nThe cache core is implemented entirely in Rust and tuned for throughput:\n\n- **Rust core** — no GC pauses on the cache itself; deterministic memory handling\n  via RAII (entries are freed the instant they leave the map).\n- **DashMap** — the map is split into multiple internal shards, each guarded by\n  its own lock, so operations on different keys proceed in parallel.\n- **Lock-free reads** for statistics — counters use atomic integers\n  (`AtomicU64`) and are incremented with relaxed atomics, avoiding a global lock.\n- **Low allocation** — values are stored as a single contiguous `Vec\u003cu8\u003e`; there\n  is no per-field boxing.\n- **Fast TTL checks** — expiration is a single integer comparison against a\n  cached millisecond timestamp.\n\n---\n\n## Architecture\n\n```txt\nNode.js\n ↓\nnapi-rs   (the Rust ⇆ V8 bridge: #[napi] generates the glue code)\n ↓\nRust Cache Engine   (set / get / ttl / stats / cleanup)\n ↓\nDashMap   (concurrent, lock-sharded HashMap\u003cString, CacheEntry\u003e)\n```\n\nValues cross the boundary as `serde_json::Value` and are serialized to JSON\nbytes (`Vec\u003cu8\u003e`) inside the engine. This keeps the storage layer agnostic to the\nshape of your data and leaves room to adopt binary formats (MessagePack, CBOR,\nBincode) later without touching the cache logic.\n\n---\n\n## API Reference\n\n| Method                         | Returns        | Description                                                       |\n| ------------------------------ | -------------- | ----------------------------------------------------------------- |\n| `new Cache(options?)`          | `Cache`        | Create a cache (`maxSize`, `evictionPolicy`, `cleanupIntervalSeconds`). |\n| `set(key, value, options?)`    | `boolean`      | Store/overwrite a value. `options.ttlSeconds` sets expiration.    |\n| `evictionPolicy` (getter)      | `string`       | The active policy: `\"reject\"` or `\"lru\"`.                         |\n| `get\u003cT\u003e(key)`                  | `T \\| null`    | Read a value (lazy-expires stale entries).                        |\n| `delete(key)`                  | `boolean`      | Remove a key. `true` if it existed.                               |\n| `exists(key)`                  | `boolean`      | Whether the key exists and is valid.                              |\n| `clear()`                      | `void`         | Remove all entries.                                               |\n| `size()`                       | `number`       | Number of keys currently stored.                                  |\n| `cleanupExpired()`             | `number`       | Sweep expired entries; returns how many were removed.             |\n| `stats()`                      | `CacheStats`   | Cumulative counters + current size.                               |\n\n### Types\n\n```ts\ninterface CacheOptions {\n  maxSize?: number;\n  evictionPolicy?: \"reject\" | \"lru\"; // default \"reject\"\n  cleanupIntervalSeconds?: number; // enables the background sweeper\n}\n\ninterface SetOptions {\n  ttlSeconds?: number;\n}\n\ninterface CacheStats {\n  hits: number;\n  misses: number;\n  sets: number;\n  deletes: number;\n  expired: number;\n  evicted: number;\n  size: number;\n}\n```\n\n---\n\n## Express Example\n\n```ts\nimport express from \"express\";\nimport { Cache } from \"rust-node-cache\";\nimport { cacheMiddleware } from \"rust-node-cache/express\";\n\nconst cache = new Cache();\nconst app = express();\n\n// Automatic response caching (adds an `X-Cache: HIT|MISS` header).\napp.get(\"/users/:id\", cacheMiddleware({ cache, ttlSeconds: 60 }), async (req, res) =\u003e {\n  const user = await database.findUser(req.params.id);\n  res.json(user);\n});\n\n// Or use the cache manually.\napp.get(\"/manual/:id\", async (req, res) =\u003e {\n  const key = `user:${req.params.id}`;\n  const cached = cache.get(key);\n  if (cached) return res.json(cached);\n\n  const user = await database.findUser(req.params.id);\n  cache.set(key, user, { ttlSeconds: 60 });\n  res.json(user);\n});\n```\n\n---\n\n## Fastify Example\n\n```ts\nimport Fastify from \"fastify\";\nimport { Cache } from \"rust-node-cache\";\nimport { cachePlugin } from \"rust-node-cache/fastify\";\n\nconst cache = new Cache();\nconst fastify = Fastify();\n\nfastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n\nfastify.get(\"/users/:id\", async (req) =\u003e {\n  return database.findUser(req.params.id);\n});\n```\n\n---\n\n## NestJS Example\n\n```ts\nimport { Cache } from \"rust-node-cache\";\nimport { CacheInterceptor } from \"rust-node-cache/nestjs\";\n\nconst cache = new Cache();\n\n// Apply globally...\napp.useGlobalInterceptors(new CacheInterceptor({ cache, ttlSeconds: 60 }));\n\n// ...or per controller/route with @UseInterceptors(new CacheInterceptor({ cache })).\n```\n\n---\n\n## Benchmarks\n\nComing soon.\n\n---\n\n## Limitations\n\n- Local process only\n- Not distributed\n- Data is lost on restart\n- Multiple workers (cluster / PM2) have separate, independent caches\n\n---\n\n## Roadmap\n\n| Version | Feature                  | Status |\n| ------- | ------------------------ | ------ |\n| v0.1    | Basic cache              | ✅     |\n| v0.2    | TTL cleanup thread + LRU eviction | ✅ |\n| v0.3    | LFU cache                | 🔜     |\n| v0.4    | Redis synchronization    | 🔜     |\n| v0.5    | Prometheus metrics       | 🔜     |\n| v0.6    | ImmutableLog integration | 🔜     |\n\n### Future: ImmutableLog Integration\n\n```ts\ncache.onEvicted((event) =\u003e {\n  immutablelog.send(event);\n});\n```\n\n```json\n{\n  \"event_type\": \"cache_evicted\",\n  \"key\": \"user:123\",\n  \"reason\": \"expired\"\n}\n```\n\n### Future: Decorator API\n\n```ts\n@Cacheable({ ttlSeconds: 60 })\nasync function getUser(id: number) {}\n```\n\n---\n\n## Development\n\n```bash\nnpm install\nnpm run build   # builds the native addon + the TypeScript layer\nnpm test\n```\n\nThe build runs in two stages:\n\n- `npm run build:native` — compiles the Rust crate and emits the platform addon\n  plus `binding.js` / `binding.d.ts` (the native loader and its types).\n- `npm run build:js` — bundles the TypeScript layer in `js/` into `dist/` (CJS +\n  ESM + type declarations) with [tsup](https://tsup.egoist.dev/).\n\n---\n\n## Publishing\n\n```bash\nnpm version patch\ngit push --tags\n```\n\nPushing a `vX.Y.Z` tag triggers CI to build every platform binary, bundle them\ninto a single self-contained package, and publish it to npm.\n\n---\n\n## License\n\nMIT © [Roberto Lima](https://github.com/robertolima-dev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertolima-dev%2Frust-node-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertolima-dev%2Frust-node-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertolima-dev%2Frust-node-cache/lists"}