{"_id":"undici-cache-redis","_rev":"2-63e541b1132ce94e18b4a024699ad352","name":"undici-cache-redis","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"undici-cache-redis","version":"1.0.0","keywords":["undici","cache","redis"],"author":{"url":"https://platformatichq.com","name":"Platformatic Inc.","email":"info@platformatic.dev"},"license":"Apache-2.0","_id":"undici-cache-redis@1.0.0","maintainers":[{"name":"matteo.collina","email":"hello@matteocollina.com"}],"homepage":"https://github.com/platformatic/undici-cache-redis#readme","bugs":{"url":"https://github.com/platformatic/undici-cache-redis/issues"},"dist":{"shasum":"6a9218313bc636e6e3f8f4e65052494180f9e05d","tarball":"https://registry.npmjs.org/undici-cache-redis/-/undici-cache-redis-1.0.0.tgz","fileCount":45,"integrity":"sha512-pzbCpQCwHRzsPKNITOqPNim/HsdlHT3WjbTksMD++OwMgcsQYQR8QZkc+i+0c+JDMz92hmioio/r4qwxqXVajA==","signatures":[{"sig":"MEUCIQC132LADwG6xVcOCFOmjwj0Hf9abpgyfmYfLcMMd4X7+AIgL0/TmyTSjvR6aFomzH5SzqKQTe64t0ZAnPRgSabTp0o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":218370},"main":"index.js","types":"./index.d.ts","gitHead":"1c727ca10d8c33f982e88768fe07a14b40c7e77e","scripts":{"lint":"eslint","test":"node --test --test-concurrency=1 ./test/*.test.js","bench":"./benchmarks/run-benchmarks.sh","valkey":"docker compose up -d","lint:fix":"eslint --fix","example:run":"cd example && ./run-example.sh","example:client":"node example/client.js","example:server":"node example/server.js","test:typescript":"tsd --files test/ && tsc ./index.d.ts --noEmit","example:advanced":"node example/advanced-client.js","example:docker:up":"cd example && docker-compose up -d","example:docker:down":"cd example && docker-compose down"},"_npmUser":{"name":"matteo.collina","email":"hello@matteocollina.com"},"repository":{"url":"git+https://github.com/platformatic/undici-cache-redis.git","type":"git"},"_npmVersion":"10.9.2","description":"An Undici cache store with a Redis backend.","directories":{"lib":"lib"},"_nodeVersion":"22.17.1","dependencies":{"lru_map":"^0.4.1","iovalkey":"^0.3.3","atomic-sleep":"^1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.32.0","eslint":"^9.32.0","undici":"^7.13.0","fastify":"^5.4.0","autocannon":"^8.0.0","typescript":"^5.9.2","@types/node":"^22.7.4","neostandard":"^0.12.0"},"_npmOperationalInternal":{"tmp":"tmp/undici-cache-redis_1.0.0_1754640330699_0.7271440727610212","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"undici-cache-redis","version":"1.0.1","description":"An Undici cache store with a Redis backend.","main":"index.js","directories":{"lib":"lib"},"scripts":{"lint":"eslint","lint:fix":"eslint --fix","valkey":"docker compose up -d","test":"node --test --test-concurrency=1 ./test/*.test.js","test:typescript":"tsd --files test/ && tsc ./index.d.ts --noEmit","example:server":"node example/server.js","example:client":"node example/client.js","example:advanced":"node example/advanced-client.js","example:docker:up":"cd example && docker-compose up -d","example:docker:down":"cd example && docker-compose down","example:run":"cd example && ./run-example.sh","bench":"./benchmarks/run-benchmarks.sh"},"repository":{"type":"git","url":"git+https://github.com/platformatic/undici-cache-redis.git"},"keywords":["undici","cache","redis"],"author":{"name":"Platformatic Inc.","email":"info@platformatic.dev","url":"https://platformatichq.com"},"license":"Apache-2.0","types":"./index.d.ts","bugs":{"url":"https://github.com/platformatic/undici-cache-redis/issues"},"homepage":"https://github.com/platformatic/undici-cache-redis#readme","dependencies":{"iovalkey":"^0.3.3","lru_map":"^0.4.1"},"devDependencies":{"atomic-sleep":"^1.0.0","@types/node":"^22.7.4","eslint":"^9.32.0","neostandard":"^0.12.0","tsd":"^0.33.0","typescript":"^5.9.2","undici":"^7.13.0","fastify":"^5.4.0","autocannon":"^8.0.0"},"_id":"undici-cache-redis@1.0.1","gitHead":"320746903963dd3c76413c5eb779d006db2bcfd8","_nodeVersion":"22.19.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-WPggGOAmthr48gaBgZLltgEUp2g+1Ej0P2DFo48MNHbVw6GeHysr/4gDtl7k5BvmAj9391J+d5b0uGrTHaYArw==","shasum":"6e5ad15ad9a4c0998d3b459e6d44261125356087","tarball":"https://registry.npmjs.org/undici-cache-redis/-/undici-cache-redis-1.0.1.tgz","fileCount":46,"unpackedSize":223117,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/undici-cache-redis@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCOsaWjhfLUvv7lWFIenOuJ3r9Tupz2JdnLVJtwDzIOIQIhAL7k6OOb8yd+9PPsYA5B7F3ENwRADgjBmVntdJlcUazS"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:671724d1-4d6d-4814-bdf2-42e4c0298dba"}},"maintainers":[{"name":"matteo.collina","email":"hello@matteocollina.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/undici-cache-redis_1.0.1_1758548783044_0.3814489193578623"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-08T08:05:30.698Z","modified":"2025-09-22T13:46:23.638Z","1.0.0":"2025-08-08T08:05:30.912Z","1.0.1":"2025-09-22T13:46:23.227Z"},"bugs":{"url":"https://github.com/platformatic/undici-cache-redis/issues"},"author":{"name":"Platformatic Inc.","email":"info@platformatic.dev","url":"https://platformatichq.com"},"license":"Apache-2.0","homepage":"https://github.com/platformatic/undici-cache-redis#readme","keywords":["undici","cache","redis"],"repository":{"type":"git","url":"git+https://github.com/platformatic/undici-cache-redis.git"},"description":"An Undici cache store with a Redis backend.","maintainers":[{"name":"matteo.collina","email":"hello@matteocollina.com"}],"readme":"# undici-cache-redis\n\nA high-performance Redis-backed cache store for [Undici's](https://github.com/nodejs/undici) cache interceptor. This library provides seamless HTTP response caching with Redis/Valkey as the storage backend, featuring client-side caching, cache invalidation by tags, and support for managed Redis environments.\n\nBuilt on top of [iovalkey](https://github.com/valkey-io/iovalkey) for optimal Redis/Valkey connectivity.\n\n## Features\n\n- 🚀 **High Performance**: Redis-backed caching with client-side optimization\n- 🏷️ **Cache Tags**: Invalidate cached responses by custom tags\n- 🔄 **Automatic Invalidation**: Smart cache invalidation on mutating operations\n- 📊 **Cache Management**: Built-in cache manager for monitoring and administration\n- 🌐 **Vary Header Support**: Proper handling of content negotiation\n- ☁️ **Cloud Ready**: Works with managed Redis services (AWS ElastiCache, etc.)\n- 💾 **Binary Support**: Handles both text and binary response data\n- 📈 **Tracking Cache**: Client-side caching for improved performance\n\n## Installation\n\n```bash\nnpm install undici-cache-redis\n```\n\n## Quick Start\n\n### Basic Usage\n\n```javascript\nimport { Agent, interceptors } from 'undici'\nimport { RedisCacheStore } from 'undici-cache-redis'\n\n// Create a Redis cache store\nconst store = new RedisCacheStore({\n  clientOpts: {\n    host: 'localhost',\n    port: 6379,\n    keyPrefix: 'my-app:cache:'\n  }\n})\n\n// Create Undici agent with caching\nconst agent = new Agent()\n  .compose(interceptors.cache({ store }))\n\n// Make requests - responses will be automatically cached\nconst response = await agent.request({\n  origin: 'https://api.example.com',\n  method: 'GET',\n  path: '/users/123'\n})\n\nconsole.log(await response.body.text())\n```\n\n### Cache Invalidation by Tags\n\n```javascript\nimport { Agent, interceptors } from 'undici'\nimport { RedisCacheStore } from 'undici-cache-redis'\n\nconst store = new RedisCacheStore({\n  cacheTagsHeader: 'cache-tags' // Header to read cache tags from\n})\n\nconst agent = new Agent()\n  .compose(interceptors.cache({ store }))\n\n// Server responds with: Cache-Tags: user:123,profile\nconst response = await agent.request({\n  origin: 'https://api.example.com',\n  method: 'GET',\n  path: '/users/123'\n})\n\n// Later, invalidate all cached responses tagged with 'user:123'\nawait store.deleteTags(['user:123'])\n```\n\n### Advanced Cache Management with RedisCacheManager\n\n```javascript\nimport { RedisCacheStore, RedisCacheManager } from 'undici-cache-redis'\n\n// Create both store and manager\nconst store = new RedisCacheStore({\n  cacheTagsHeader: 'cache-tags'\n})\n\nconst manager = new RedisCacheManager({\n  clientOpts: { host: 'localhost', port: 6379 }\n})\n\n// Subscribe to cache events\nawait manager.subscribe()\n\nmanager.on('add-entry', (entry) => {\n  console.log('Cache entry added:', entry.path, entry.cacheTags)\n})\n\nmanager.on('delete-entry', ({ id, keyPrefix }) => {\n  console.log('Cache entry deleted:', id)\n})\n\n// Analyze cache contents\nawait manager.streamEntries((entry) => {\n  console.log(`Entry: ${entry.path}, Tags: [${entry.cacheTags.join(', ')}]`)\n}, '')\n\n// Invalidate by tags using the store\nawait store.deleteTags(['user:123', 'products'])\n\n// Clean up specific entries by ID\nconst entriesToDelete = []\nawait manager.streamEntries((entry) => {\n  if (entry.path.startsWith('/api/products/')) {\n    entriesToDelete.push(entry.id)\n  }\n}, '')\n\nif (entriesToDelete.length > 0) {\n  await manager.deleteIds(entriesToDelete, '')\n}\n\n// Get response body for debugging\nconst responseBody = await manager.getResponseById('some-entry-id', '')\n```\n\n### Cache Management\n\n```javascript\nimport { RedisCacheManager } from 'undici-cache-redis'\n\nconst manager = new RedisCacheManager({\n  clientOpts: {\n    host: 'localhost',\n    port: 6379\n  }\n})\n\n// Subscribe to cache events\nawait manager.subscribe()\n\nmanager.on('add-entry', (entry) => {\n  console.log('Cache entry added:', entry.id)\n})\n\nmanager.on('delete-entry', ({ id, keyPrefix }) => {\n  console.log('Cache entry deleted:', id)\n})\n\n// Stream all cache entries\nawait manager.streamEntries((entry) => {\n  console.log('Entry:', entry.origin, entry.path, entry.statusCode)\n}, 'my-app:cache:')\n\n// Get response body by ID\nconst responseBody = await manager.getResponseById('entry-id', 'my-app:cache:')\n```\n\n## Configuration Options\n\n### RedisCacheStore Options\n\n```typescript\ninterface RedisCacheStoreOpts {\n  // Redis client options (passed to iovalkey)\n  clientOpts?: {\n    host?: string\n    port?: number\n    keyPrefix?: string\n    // ... other iovalkey options\n  }\n  \n  // Maximum size in bytes for a single cached response\n  maxEntrySize?: number\n  \n  // Maximum total cache size (for client-side cache)\n  maxSize?: number\n  \n  // Maximum number of entries (for client-side cache)\n  maxCount?: number\n  \n  // Enable/disable client-side tracking cache (default: true)\n  tracking?: boolean\n  \n  // Header name to read cache tags from responses\n  cacheTagsHeader?: string\n  \n  // Error callback function\n  errorCallback?: (err: Error) => void\n}\n```\n\n### RedisCacheManager Options\n\n```typescript\ninterface RedisCacheManagerOpts {\n  // Redis client options\n  clientOpts?: {\n    host?: string\n    port?: number\n    // ... other iovalkey options\n  }\n  \n  // Whether to configure keyspace event notifications (default: true)\n  // Set to false for managed Redis services\n  clientConfigKeyspaceEventNotify?: boolean\n}\n```\n\n## Advanced Usage Examples\n\n### Using with fetch()\n\n```javascript\nimport { Agent, interceptors, setGlobalDispatcher } from 'undici'\nimport { RedisCacheStore } from 'undici-cache-redis'\n\n// Create a Redis cache store\nconst store = new RedisCacheStore()\n\n// Create agent with caching\nconst agent = new Agent()\n  .compose(interceptors.cache({ store }))\n\n// Set as global dispatcher to enable caching for fetch\nsetGlobalDispatcher(agent)\n\n// Now fetch() automatically uses the cache!\nconst response = await fetch('https://api.example.com/users/123')\nconst data = await response.json()\n\n// Cache headers are available\nif (response.headers.get('x-cache') === 'HIT') {\n  console.log('Response was served from cache!')\n}\n```\n\n### Working with Vary Headers\n\n```javascript\nconst store = new RedisCacheStore()\nconst agent = new Agent()\n  .compose(interceptors.cache({ store }))\n\n// Different responses cached based on Accept-Language header\nconst responseEn = await agent.request({\n  origin: 'https://api.example.com',\n  method: 'GET',\n  path: '/content',\n  headers: { 'Accept-Language': 'en' }\n})\n\nconst responseFr = await agent.request({\n  origin: 'https://api.example.com',\n  method: 'GET',\n  path: '/content',\n  headers: { 'Accept-Language': 'fr' }\n})\n```\n\n### Manual Cache Operations\n\n```javascript\nconst store = new RedisCacheStore()\n\n// Delete specific cache entries\nawait store.deleteKeys([\n  { origin: 'https://api.example.com', method: 'GET', path: '/users/123' }\n])\n\n// Delete by cache tags\nawait store.deleteTags(['user:123', 'profile'])\n\n// Close the store when done\nawait store.close()\n```\n\n### Error Handling\n\n```javascript\nconst store = new RedisCacheStore({\n  errorCallback: (err) => {\n    console.error('Cache error:', err.message)\n    // Send to monitoring service\n    monitoringService.error('cache_error', err)\n  }\n})\n```\n\n## Managed Redis Services\n\nWhen using managed Redis services like AWS ElastiCache, some Redis commands may be restricted. Configure the cache manager accordingly:\n\n```javascript\nconst manager = new RedisCacheManager({\n  clientConfigKeyspaceEventNotify: false, // Disable auto-configuration\n  clientOpts: {\n    host: 'your-elasticache-endpoint.cache.amazonaws.com',\n    port: 6379\n  }\n})\n```\n\nEnsure your managed Redis instance has the following configuration:\n- `notify-keyspace-events AKE` (if not automatically configured)\n\n## Multi-Host Architecture\n\n```mermaid\ngraph TB\n    subgraph \"Users\"\n        U1[User 1]\n        U2[User 2]\n        U3[User N]\n    end\n    \n    subgraph \"Host 1\"\n        A1[App]\n        B1[Local Cache]\n    end\n    \n    subgraph \"Host 2\" \n        A2[App]\n        B2[Local Cache]\n    end\n    \n    subgraph \"Host N\"\n        A3[App]\n        B3[Local Cache]\n    end\n    \n    subgraph \"Redis/Valkey\"\n        R[Shared Cache Storage<br/>+ Invalidation Events]\n    end\n    \n    subgraph \"External APIs\"\n        API[HTTP APIs]\n    end\n    \n    U1 --> A1\n    U2 --> A2\n    U3 --> A3\n    \n    A1 <--> B1\n    A2 <--> B2\n    A3 <--> B3\n    \n    B1 <--> R\n    B2 <--> R\n    B3 <--> R\n    \n    A1 --> API\n    A2 --> API\n    A3 --> API\n    \n    R -.-> B1\n    R -.-> B2\n    R -.-> B3\n    \n    classDef users fill:#e8f5e8\n    classDef app fill:#e3f2fd\n    classDef cache fill:#f3e5f5\n    classDef redis fill:#ffebee\n    classDef api fill:#fff3e0\n    \n    class U1,U2,U3 users\n    class A1,A2,A3 app\n    class B1,B2,B3 cache\n    class R redis\n    class API api\n```\n\n**Flow**: Users make requests → Apps check local/Redis cache → If miss, fetch from APIs → Cache responses → Invalidation events sync all hosts.\n\n## Cache Key Structure\n\nThe library uses a structured approach to Redis keys:\n\n- **Metadata keys**: `{prefix}metadata:{origin}:{path}:{method}:{id}`\n- **Value keys**: `{prefix}values:{id}`\n- **ID keys**: `{prefix}ids:{id}`\n- **Tag keys**: `{prefix}cache-tags:{tag1}:{tag2}:{id}`\n\nWhere `{prefix}` is your configured `keyPrefix` and `{id}` is a UUID for each cache entry.\n\n## Cache Invalidation Flow\n\nThe following diagram illustrates how cache invalidation works across different scenarios:\n\n```mermaid\nflowchart TD\n    A[Cache Invalidation Request] --> B{Invalidation Type}\n    \n    B -->|Delete by Key| C[delete key]\n    B -->|Delete by Tags| D[deleteTags tags]\n    B -->|Automatic Cleanup| E[Redis Expiration Events]\n    \n    C --> F[Find Metadata Keys]\n    F --> G[Get Metadata from Redis]\n    G --> H[Extract Associated Keys]\n    H --> I[Delete Redis Keys]\n    I --> JJ[Redis/Valkey Key Deleted]\n    JJ --> J[Update Tracking Cache]\n    J --> K[Clean up Tags]\n    K --> L[Complete]\n    \n    D --> M[Scan for Tag Patterns]\n    M --> N[Find Matching Tag Keys]\n    N --> O[Get Metadata References]\n    O --> P[Delete Tag Keys]\n    P --> PP[Redis/Valkey Keys Deleted]\n    PP --> Q[Delete Referenced Entries]\n    Q --> R[Update Tracking Cache]\n    R --> S[Complete]\n    \n    E --> T[Redis Keyspace Event]\n    T --> U{Event Type}\n    U -->|expired| V[Entry Expiration]\n    U -->|del| W[Manual Deletion]\n    \n    V --> X[Parse Key Type]\n    X --> Y{Key Type}\n    Y -->|metadata| Z[Emit delete-entry Event]\n    Y -->|cache-tags| AA[Parse Tags from Key]\n    AA --> BB[Global Tag Cleanup]\n    BB --> CC[Delete Tag Entries]\n    CC --> DD[Complete]\n    \n    W --> X\n    Z --> DD\n    \n    %% Client-side tracking invalidation triggered by Redis updates\n    JJ --> EE[Redis Client Tracking Detects Change]\n    PP --> EE\n    EE --> FF[__redis__:invalidate Event]\n    FF --> GG[Parse Metadata Key]\n    GG --> HH[Remove from Tracking Cache]\n    HH --> II[Tracking Complete]\n    \n    %% Styling\n    classDef primary fill:#e1f5fe\n    classDef process fill:#f3e5f5\n    classDef decision fill:#fff3e0\n    classDef complete fill:#e8f5e8\n    classDef redis fill:#ffebee\n    \n    class A,C,D,E primary\n    class F,G,H,I,J,K,M,N,O,P,Q,R,T,V,W,X,AA,BB,CC,FF,GG,HH process\n    class B,U,Y decision\n    class L,S,DD,II complete\n    class JJ,PP,EE redis\n```\n\n### Invalidation Methods\n\n1. **Direct Key Deletion**: Targets specific cache entries by URL pattern\n2. **Tag-based Deletion**: Removes all entries associated with given cache tags\n3. **Automatic Expiration**: Handles Redis TTL expiration and manual deletions\n4. **Client-side Tracking**: Maintains local cache consistency via Redis invalidation notifications\n\n## Performance Considerations\n\n1. **Client-side Tracking**: Enabled by default, provides in-memory caching of metadata\n2. **Pipeline Operations**: Uses Redis pipelining for batch operations\n3. **Binary Data**: Efficiently handles binary responses with base64 encoding\n4. **Memory Management**: Configurable size limits prevent memory exhaustion\n\n## API Reference\n\n### RedisCacheStore\n\n#### Methods\n\n- `get(key: CacheKey): Promise<GetResult | undefined>` - Retrieve cached response\n- `createWriteStream(key: CacheKey, value: CachedResponse): Writable` - Create write stream for caching\n- `delete(key: CacheKey): Promise<void>` - Delete cache entries by key pattern\n- `deleteKeys(keys: CacheKey[]): Promise<void>` - Delete multiple cache entries\n- `deleteTags(tags: string[]): Promise<void>` - Delete entries by cache tags\n- `close(): Promise<void>` - Close Redis connections\n\n#### Events\n\n- `write` - Emitted when a cache entry is written\n\n### RedisCacheManager\n\n#### Methods\n\n- `streamEntries(callback, keyPrefix): Promise<void>` - Stream all cache entries\n- `subscribe(): Promise<void>` - Subscribe to cache events\n- `getResponseById(id, keyPrefix): Promise<string | null>` - Get response body by ID\n- `getDependentEntries(id, keyPrefix): Promise<CacheEntry[]>` - Get entries sharing cache tags\n- `deleteIds(ids, keyPrefix): Promise<void>` - Delete entries by IDs\n- `close(): Promise<void>` - Close connections\n\n#### Events\n\n- `add-entry` - Emitted when a cache entry is added\n- `delete-entry` - Emitted when a cache entry is deleted\n- `error` - Emitted on errors\n\n## Troubleshooting\n\n### Common Issues\n\n**Connection Errors**\n```javascript\n// Ensure Redis is running and accessible\nconst store = new RedisCacheStore({\n  clientOpts: {\n    host: 'localhost',\n    port: 6379,\n    connectTimeout: 10000,\n    retryDelayOnFailover: 100\n  }\n})\n```\n\n**Memory Issues**\n```javascript\n// Limit cache size to prevent memory exhaustion\nconst store = new RedisCacheStore({\n  maxEntrySize: 1024 * 1024, // 1MB per entry\n  maxSize: 100 * 1024 * 1024, // 100MB total\n  maxCount: 10000 // Max 10k entries\n})\n```\n\n**Managed Redis Issues**\n```javascript\n// For AWS ElastiCache or similar services\nconst manager = new RedisCacheManager({\n  clientConfigKeyspaceEventNotify: false,\n  clientOpts: {\n    host: 'your-cluster.cache.amazonaws.com',\n    port: 6379,\n    family: 4, // Force IPv4\n    enableReadyCheck: false\n  }\n})\n```\n\n## Requirements\n\n- Node.js >= 20\n- Redis >= 6.0 or Valkey >= 7.2\n- Undici >= 7.0\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n## Benchmarking\n\nThis project includes comprehensive benchmarks to measure performance improvements with different caching strategies.\n\n### Quick Benchmark\n```bash\n# Automated benchmark with all prerequisites checked\n./run-benchmarks.sh\n\n# Or run manually\nnpm run bench\n```\n\nThe benchmarks test a realistic proxy server architecture:\n- **Server Foo (Proxy)**: Uses Undici with different cache configurations  \n- **Server Bar (Backend)**: API server with simulated latency\n- **Autocannon**: Load testing tool measuring performance\n\nExpected results show **10-15x performance improvement** with caching enabled.\n\nFor detailed benchmarking instructions, see [benchmarks/README.md](./benchmarks/README.md).\n## Contributing\n\nThis project is part of the Platformatic ecosystem. For contributing guidelines, please refer to the main [Platformatic repository](https://github.com/platformatic/platformatic).\n\n## Related Projects\n\n- [Undici](https://github.com/nodejs/undici) - HTTP/1.1 client for Node.js\n- [iovalkey](https://github.com/valkey-io/iovalkey) - High-performance Valkey client\n- [Platformatic](https://github.com/platformatic/platformatic) - Enterprise-Ready Node.js\n","readmeFilename":"README.md"}