{"id":15375562,"url":"https://github.com/etienne-martin/express-aggressive-cache","last_synced_at":"2025-04-15T15:13:51.980Z","repository":{"id":43464310,"uuid":"240594896","full_name":"etienne-martin/express-aggressive-cache","owner":"etienne-martin","description":"An aggressive yet obedient cache middleware for express","archived":false,"fork":false,"pushed_at":"2022-03-01T15:23:58.000Z","size":3434,"stargazers_count":7,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T21:42:42.508Z","etag":null,"topics":["api","cache","express","json","memory","middleware","redis","response","ssr"],"latest_commit_sha":null,"homepage":"","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/etienne-martin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["etienne-martin"]}},"created_at":"2020-02-14T20:40:26.000Z","updated_at":"2021-11-15T17:00:47.000Z","dependencies_parsed_at":"2022-09-21T01:40:29.554Z","dependency_job_id":null,"html_url":"https://github.com/etienne-martin/express-aggressive-cache","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etienne-martin%2Fexpress-aggressive-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etienne-martin%2Fexpress-aggressive-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etienne-martin%2Fexpress-aggressive-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etienne-martin%2Fexpress-aggressive-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/etienne-martin","download_url":"https://codeload.github.com/etienne-martin/express-aggressive-cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248743982,"owners_count":21154782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["api","cache","express","json","memory","middleware","redis","response","ssr"],"created_at":"2024-10-01T14:04:00.471Z","updated_at":"2025-04-15T15:13:51.950Z","avatar_url":"https://github.com/etienne-martin.png","language":"TypeScript","funding_links":["https://github.com/sponsors/etienne-martin"],"categories":[],"sub_categories":[],"readme":"# express aggressive cache\n\nAn aggressive yet obedient cache middleware for express.\n\n[![Coveralls github](https://img.shields.io/coveralls/github/etienne-martin/express-aggressive-cache.svg)](https://coveralls.io/github/etienne-martin/express-aggressive-cache)\n[![CircleCI build](https://img.shields.io/circleci/project/github/etienne-martin/express-aggressive-cache.svg)](https://circleci.com/gh/etienne-martin/express-aggressive-cache)\n[![node version](https://img.shields.io/node/v/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)\n[![npm version](https://img.shields.io/npm/v/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)\n[![npm monthly downloads](https://img.shields.io/npm/dm/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)\n\n## Features\n\n- Plug and Play\n- Built-in TypeScript support\n- Multiple data stores (in-memory and Redis)\n- Thoroughly tested\n\n## Getting Started\n\n### Installation\n\nTo use express-aggressive-cache in your project, run:\n\n```bash\nnpm install express-aggressive-cache\n```\n\n### Usage\n\n**Example** - application-wide caching:\n\n```javascript\nimport express from \"express\";\nimport cache from \"express-aggressive-cache\";\n\nconst app = express();\n\napp.use(\n  cache({\n    maxAge: 3600\n  }).middleware\n);\n\napp.get(\"/hello\", (req, res) =\u003e {\n  res.json({\n    hello: \"world\"\n  });\n});\n```\n\n**Example** - caches a specific endpoint:\n\n```javascript\nimport express from \"express\";\nimport cache from \"express-aggressive-cache\";\n\nconst app = express();\n\napp.get(\"/hello\", cache().middleware, (req, res) =\u003e {\n  res.json({\n    hello: \"world\"\n  });\n});\n```\n\n## Cache Control\n\nThis middleware uses the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header to determine whether or not a response should be cached and for how long.\n\nYou can specify for how long a response should be cached by specifying a `max-age` or `s-maxage` with a value greater than zero.\n\n**Responses that do not specify a `max-age` or `s-maxage` will be cached by default.**\n\nResponses containing `no-store`, `private`, `max-age=0` or `s-maxage=0` won't be cached. **Anything else will be cached.**\n\nThe `s-maxage` directive always takes precedence over the `max-age` directive.\n\n## Purge function\n\nThe cache object provides an *async* purge function to remove a cache entry based on a previously passed cache tag. See the `getCacheTag` option. The cache entry associated with the cache tag will be removed immediately. It will take at least a minute for all memory associated with the cache entry to be freed.\n\n**Example** - purge the cache entry for a unique cache tag previously returned by `getCacheTag`\n\n```javascript\nimport cache from \"express-aggressive-cache\";\n\nconst myCache = cache()\n\n// usage of myCache.middleware\n\nconst purgeEndpoint = async () =\u003e {\n  await myCache.purge(\"c341fbb1-a6f6-4a21-8949-c84017da27dd\");\n}\n\n```\n\n\n## x-cache\n\nBy default, the middleware will set a response header `x-cache`. Its value will be `HIT` when a cache hit occurs and the response was obtained from cache otherwise it will be `MISS`. See the `onCacheHit` and `onCacheMiss` options to override this behavior.\n\n## Options\n\n#### `maxAge`\n\nIf the response has a `max-age` header, it will use it as the TTL. Otherwise, it will expire the resource using the `maxAge` option (defaults to Infinity). Value should be provided in seconds.\n\n#### `store`\n\nSpecify a different data store. Default to in-memory caching.\n\n### `memoryStore(options)`\n\nBy default, the cache will be stored in memory (RAM). Since everything is stored in memory, the more cache, the higher the RAM usage. You can use the `max` option to mitigate this. It will delete the least-recently-used items as it reaches the limit.\n\nNote: In-memory caching is not suitable for applications that scale horizontally as the cache will be duplicated across multiple nodes and can result in a high memory usage within your cluster.\n\n**We recommend using a Redis data store if you have multiple instances of your application running behind a load balancer.**\n\n#### `max`\n\nThe maximum size of the cache, checked by applying the length function to all values in the cache. Defaults to `Infinity`.\n\n**Example** - limit the amount of entries in the cache:\n\n```javascript\ncache({\n  store: memoryStore({\n    max: 500\n  })\n});\n```\n\n---\n\n### `redisStore(options)`\n\nIt is recommended that Redis be configured with a `allkeys-lru` eviction policy to prevent random keys from being deleted while serving responses.\n\nNote: performance will be impacted when caching large responses like files and images. **We do not recommend caching anything above 5mb.**\n\n#### `client`\n\nAn instance of Redis or a Redis compatible client.\n\nKnown compatible and tested clients:\n\n- [ioredis](https://www.npmjs.com/package/ioredis)\n\n#### `prefix`\n\nKey prefix in Redis (default: \"cache\").\n\nThis prefix appends to whatever prefix you may have set on the client itself.\n\nNote: You may need unique prefixes for different applications sharing the same Redis instance.\n\n**Example** - store the cache in redis:\n\n```javascript\ncache({\n  store: redisStore({\n    client: new Redis(\"//localhost:6379\"),\n    prefix: \"api-cache\"\n  })\n});\n```\n\n---\n\n#### `getCacheKey`\n\nFunction used to generate cache keys.\n\nIt determines how the cache key should be computed, receiving `req`, `res` and `normalizedPath` as input.\n\nCan be useful if you need to cache multiple variants of the same resource depending on the specifics of your application.\n\n**Example** - cache authenticated and non-authenticated requests separately:\n\n```javascript\ncache({\n  getCacheKey: ({ req, normalizedPath }) =\u003e {\n    const isAuthenticated = !!req.session.user;\n\n    return `${isAuthenticated}:${normalizedPath}`;\n  }\n});\n```\n\n**Example** - invalidate the cache when pushing a new version of your app/api:\n\n```javascript\ncache({\n  getCacheKey: ({ normalizedPath }) =\u003e {\n    const appVersion = process.env.npm_package_version;\n\n    return `${appVersion}:${normalizedPath}`;\n  }\n});\n```\n\n#### `getCacheTag`\n\nFunction to provide the purge tag which will be associated to the cache entry. The tag can later be used with the cache `purge` function. The cache tag should be unique for the cache entry. If not, only the latest cache entry will be purgeable.\n\nIt receives `req` and `res` as input. It should return `undefined` if there is no tag for the cache entry.\n\n**Example** - Sample based on Akamai's `Edge-Cache-Tag` response header:\n\n```javascript\ncache({\n  getCacheTag: ({ res }): string | undefined =\u003e {\n    return res.get(\"edge-cache-tag\");\n  }\n});\n```\n\n#### `onCacheHit` and `onCacheMiss`\n\nFunctions to perform a behavior on a cache hit or miss. For example: set a response header.\n\nIf not passed, the following default functions are used:\n\n```javascript\nconst onCacheHit: OnCache = ({ req, res }) =\u003e {\n  res.setHeader(\"x-cache\", \"HIT\");\n};\n\nconst onCacheMiss: OnCache = ({ req, res }) =\u003e {\n  res.setHeader(\"x-cache\", \"MISS\");\n};\n```\n\n#### `debug`\n\nA flag to toggle debug logs. Defaults to `false`.\n\n## TypeScript\n\nType definitions are included in this library and exposed via:\n\n```typescript\nimport {\n  ExpressAggressiveCacheOptions,\n  GetCacheKey\n} from \"express-aggressive-cache\";\n```\n\n## Built with\n\n- [node.js](https://nodejs.org/en/) - Cross-platform JavaScript run-time environment for executing JavaScript code server-side.\n- [TypeScript](https://www.typescriptlang.org/) - Typed superset of JavaScript that compiles to plain JavaScript.\n- [Jest](https://facebook.github.io/jest/) - Delightful JavaScript Testing.\n\n## Contributing\n\nWhen contributing to this project, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.\n\nUpdate the [README.md](https://github.com/etienne-martin/express-aggressive-cache/blob/master/README.md) with details of changes to the library.\n\nExecute `yarn test` and update the tests if needed.\n\n### Testing\n\nRun the full test suite:\n\n```bash\nyarn test\n```\n\nRun tests in watch mode:\n\n```bash\nyarn test:watch\n```\n\nYou can also run the following command to start the http server that is used when executing the tests:\n\n```bash\nyarn test:server\n``` \n\nWill be accessible via http://localhost:3000\n\n#### Local Redis Server\n\nA local Redis instance is needed when running the test suite. You can use the provided [redis.sh](https://github.com/etienne-martin/express-aggressive-cache/blob/master/redis.sh) script to run a Redis container using docker (Make sure docker is installed and running).\n\n```bash\n./redis.sh\n```\n\n## Authors\n\n- **Etienne Martin** - _Initial work_ - [etiennemartin.ca](http://etiennemartin.ca/)\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetienne-martin%2Fexpress-aggressive-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fetienne-martin%2Fexpress-aggressive-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetienne-martin%2Fexpress-aggressive-cache/lists"}