@fastify/caching 9.0.2 → 9.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{Readme.md → README.md} +25 -9
- package/index.js +2 -2
- package/package.json +32 -2
- package/test/cache.test.js +17 -17
- package/test/headers.test.js +14 -14
- package/types/index.test-d.ts +3 -3
package/{Readme.md → README.md}
RENAMED
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
that provides server-side caching and mechanisms for manipulating HTTP cache headers according to
|
|
9
9
|
[RFC 2616 §14.9](https://tools.ietf.org/html/rfc2616#section-14.9).
|
|
10
10
|
|
|
11
|
-
Supports Fastify versions ^3.0.0. Version v5.x supports Fastify ^3.0.0 in
|
|
12
|
-
the [`v5.x` branch](https://github.com/fastify/fastify-caching/tree/v5.x).
|
|
13
|
-
|
|
14
11
|
This plugin fully supports Fastify's encapsulation. Therefore, routes that
|
|
15
12
|
should have differing cache settings should be registered within different
|
|
16
13
|
contexts.
|
|
@@ -19,14 +16,33 @@ In addition to providing header manipulation, the plugin also decorates the
|
|
|
19
16
|
server instance with an object that can be used for caching items. **Note:**
|
|
20
17
|
the default cache should not be used in a "production" environment. It is
|
|
21
18
|
an LRU, in-memory cache that is capped at 100,000 items. It is *highly*
|
|
22
|
-
recommended that a full
|
|
19
|
+
recommended that a full-featured cache object be supplied, e.g.
|
|
23
20
|
[abstract-cache-redis][acache-redis].
|
|
24
21
|
|
|
25
22
|
[acache-redis]: https://www.npmjs.com/package/abstract-cache-redis
|
|
26
23
|
|
|
24
|
+
## Install
|
|
25
|
+
```
|
|
26
|
+
npm i @fastify/caching
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Compatibility
|
|
30
|
+
| Plugin version | Fastify version |
|
|
31
|
+
| ---------------|-----------------|
|
|
32
|
+
| `^9.x` | `^5.x` |
|
|
33
|
+
| `^8.x` | `^4.x` |
|
|
34
|
+
| `^6.x` | `^3.x` |
|
|
35
|
+
| `^4.x` | `^2.x` |
|
|
36
|
+
| `^1.x` | `^1.x` |
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin
|
|
40
|
+
in the table above.
|
|
41
|
+
See [Fastify's LTS policy](https://github.com/fastify/fastify/blob/main/docs/Reference/LTS.md) for more details.
|
|
42
|
+
|
|
27
43
|
## Example
|
|
28
44
|
|
|
29
|
-
This example shows using the plugin to disable client
|
|
45
|
+
This example shows using the plugin to disable client-side caching of all
|
|
30
46
|
routes.
|
|
31
47
|
|
|
32
48
|
```js
|
|
@@ -107,7 +123,7 @@ for a *cache-response-directive* as defined by RFC 2616.
|
|
|
107
123
|
resource may be cached. When this is set, and `privacy` is not set to `no-cache`,
|
|
108
124
|
then `', max-age=<value>'` will be appended to the `cache-control` header.
|
|
109
125
|
+ `cache` (Default: `abstract-cache.memclient`): an [abstract-cache][acache]
|
|
110
|
-
protocol
|
|
126
|
+
protocol-compliant cache object. Note: the plugin requires a cache instance to
|
|
111
127
|
properly support the ETag mechanism. Therefore, if a falsy value is supplied
|
|
112
128
|
the default will be used.
|
|
113
129
|
+ `cacheSegment` (Default: `'@fastify/caching'`): segment identifier to use when
|
|
@@ -128,7 +144,7 @@ recommended to always supply a value to this method to avoid this operation.
|
|
|
128
144
|
All incoming requests to paths affected by this plugin will be inspected for
|
|
129
145
|
the `if-none-match` header. If the header is present, the value will be used
|
|
130
146
|
to lookup the tag within the cache associated with this plugin. If the tag is
|
|
131
|
-
found, then the response will
|
|
147
|
+
found, then the response will end with a 304 status code sent to
|
|
132
148
|
the client.
|
|
133
149
|
|
|
134
150
|
Tags will be cached according to the second parameter. If the second parameter
|
|
@@ -142,11 +158,11 @@ milliseconds. The default lifetime, when the parameter is not specified, is
|
|
|
142
158
|
### `reply.expires(date)`
|
|
143
159
|
|
|
144
160
|
This method allows setting of the `expires` header. It accepts a regular `Date`
|
|
145
|
-
object
|
|
161
|
+
object or a string that is a valid date string according to
|
|
146
162
|
[RFC 2616 §14.21][sec14.21].
|
|
147
163
|
|
|
148
164
|
[sec14.21]: https://tools.ietf.org/html/rfc2616#section-14.21
|
|
149
165
|
|
|
150
166
|
## License
|
|
151
167
|
|
|
152
|
-
[MIT
|
|
168
|
+
Licensed under [MIT](./LICENSE).
|
package/index.js
CHANGED
|
@@ -38,7 +38,7 @@ function cachingLoadByEtag (req, res, next) {
|
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function cachingStoreByEtag (
|
|
41
|
+
function cachingStoreByEtag (_req, res, payload, next) {
|
|
42
42
|
const etag = res.getHeader('etag')
|
|
43
43
|
if (!etag || !res._etagLife) return next()
|
|
44
44
|
this.cache.set(
|
|
@@ -70,7 +70,7 @@ function fastifyCaching (instance, options, next) {
|
|
|
70
70
|
value += `, s-maxage=${_options.serverExpiresIn}`
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
instance.addHook('onRequest', function cachingSetCacheControlHeader (
|
|
73
|
+
instance.addHook('onRequest', function cachingSetCacheControlHeader (_req, res, next) {
|
|
74
74
|
if (!res.hasHeader('Cache-control')) {
|
|
75
75
|
res.header('Cache-control', value)
|
|
76
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/caching",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.3",
|
|
4
4
|
"description": "A plugin for Fastify to enable management of cache control headers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -28,17 +28,47 @@
|
|
|
28
28
|
"caching"
|
|
29
29
|
],
|
|
30
30
|
"author": "James Sumners <james.sumners@gmail.com>",
|
|
31
|
+
"contributors": [
|
|
32
|
+
{
|
|
33
|
+
"name": "Matteo Collina",
|
|
34
|
+
"email": "hello@matteocollina.com"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "Manuel Spigolon",
|
|
38
|
+
"email": "behemoth89@gmail.com"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "Aras Abbasi",
|
|
42
|
+
"email": "aras.abbasi@gmail.com"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "Frazer Smith",
|
|
46
|
+
"email": "frazer.dev@icloud.com",
|
|
47
|
+
"url": "https://github.com/fdawgs"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
31
50
|
"license": "MIT",
|
|
32
51
|
"bugs": {
|
|
33
52
|
"url": "https://github.com/fastify/fastify-caching/issues"
|
|
34
53
|
},
|
|
35
54
|
"homepage": "https://github.com/fastify/fastify-caching#readme",
|
|
55
|
+
"funding": [
|
|
56
|
+
{
|
|
57
|
+
"type": "github",
|
|
58
|
+
"url": "https://github.com/sponsors/fastify"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "opencollective",
|
|
62
|
+
"url": "https://opencollective.com/fastify"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
36
65
|
"devDependencies": {
|
|
37
66
|
"@fastify/pre-commit": "^2.1.0",
|
|
38
67
|
"@types/node": "^22.0.0",
|
|
39
68
|
"c8": "^10.1.2",
|
|
69
|
+
"eslint": "^9.17.0",
|
|
40
70
|
"fastify": "^5.0.0",
|
|
41
|
-
"neostandard": "^0.
|
|
71
|
+
"neostandard": "^0.12.0",
|
|
42
72
|
"tsd": "^0.31.0"
|
|
43
73
|
},
|
|
44
74
|
"dependencies": {
|
package/test/cache.test.js
CHANGED
|
@@ -21,25 +21,25 @@ test('cache is usable', async (t) => {
|
|
|
21
21
|
t.plan(4)
|
|
22
22
|
|
|
23
23
|
const fastify = Fastify()
|
|
24
|
-
await fastify.register(async (instance
|
|
25
|
-
instance.addHook('onRequest', async function checkCachingRegistered (
|
|
24
|
+
await fastify.register(async (instance) => {
|
|
25
|
+
instance.addHook('onRequest', async function checkCachingRegistered () {
|
|
26
26
|
t.assert.ifError(instance[Symbol.for('fastify-caching.registered')])
|
|
27
27
|
})
|
|
28
28
|
})
|
|
29
29
|
await fastify.register(plugin)
|
|
30
30
|
|
|
31
|
-
fastify.addHook('onRequest', async function checkCachingRegistered (
|
|
31
|
+
fastify.addHook('onRequest', async function checkCachingRegistered () {
|
|
32
32
|
t.assert.strictEqual(this[Symbol.for('fastify-caching.registered')], true)
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
fastify.get('/one', (
|
|
35
|
+
fastify.get('/one', (_req, reply) => {
|
|
36
36
|
fastify.cache.set('one', { one: true }, 1000, (err) => {
|
|
37
37
|
if (err) return reply.send(err)
|
|
38
38
|
reply.redirect('/two')
|
|
39
39
|
})
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
fastify.get('/two', (
|
|
42
|
+
fastify.get('/two', (_req, reply) => {
|
|
43
43
|
fastify.cache.get('one', (err, obj) => {
|
|
44
44
|
t.assert.ifError(err)
|
|
45
45
|
t.assert.strictEqual(obj.item, { one: true })
|
|
@@ -70,25 +70,25 @@ test('cache is usable with function as plugin default options input', async (t)
|
|
|
70
70
|
t.plan(4)
|
|
71
71
|
|
|
72
72
|
const fastify = Fastify()
|
|
73
|
-
await fastify.register(async (instance
|
|
74
|
-
instance.addHook('onRequest', async function checkCachingNotRegistered (
|
|
73
|
+
await fastify.register(async (instance) => {
|
|
74
|
+
instance.addHook('onRequest', async function checkCachingNotRegistered () {
|
|
75
75
|
t.assert.failure(instance[Symbol.for('fastify-caching.registered')])
|
|
76
76
|
})
|
|
77
77
|
})
|
|
78
78
|
await fastify.register(plugin, () => () => {})
|
|
79
79
|
|
|
80
|
-
fastify.addHook('onRequest', async function checkCachingRegistered (
|
|
80
|
+
fastify.addHook('onRequest', async function checkCachingRegistered () {
|
|
81
81
|
t.assert.strictEqual(this[Symbol.for('fastify-caching.registered')], true)
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
fastify.get('/one', (
|
|
84
|
+
fastify.get('/one', (_req, reply) => {
|
|
85
85
|
fastify.cache.set('one', { one: true }, 1000, (err) => {
|
|
86
86
|
if (err) return reply.send(err)
|
|
87
87
|
reply.redirect('/two')
|
|
88
88
|
})
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
-
fastify.get('/two', (
|
|
91
|
+
fastify.get('/two', (_req, reply) => {
|
|
92
92
|
fastify.cache.get('one', (err, obj) => {
|
|
93
93
|
t.assert.ifError(err)
|
|
94
94
|
t.assert.strictEqual(obj.item, { one: true })
|
|
@@ -120,21 +120,21 @@ test('getting cache item with error returns error', async (t) => {
|
|
|
120
120
|
t.plan(1)
|
|
121
121
|
|
|
122
122
|
const mockCache = {
|
|
123
|
-
get: (
|
|
124
|
-
set: (
|
|
123
|
+
get: (_info, callback) => callback(new Error('cache.get always errors')),
|
|
124
|
+
set: (_key, _value, _ttl, callback) => callback()
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
const fastify = Fastify()
|
|
128
128
|
await fastify.register(plugin, { cache: mockCache })
|
|
129
129
|
|
|
130
|
-
fastify.get('/one', (
|
|
130
|
+
fastify.get('/one', (_req, reply) => {
|
|
131
131
|
fastify.cache.set('one', { one: true }, 1000, (err) => {
|
|
132
132
|
if (err) return reply.send(err)
|
|
133
133
|
return reply.etag('123456').send({ hello: 'world' })
|
|
134
134
|
})
|
|
135
135
|
})
|
|
136
136
|
|
|
137
|
-
fastify.get('/two', (
|
|
137
|
+
fastify.get('/two', () => {
|
|
138
138
|
fastify.cache.get('one', (err, obj) => {
|
|
139
139
|
t.assert.failure(err)
|
|
140
140
|
t.assert.failure(obj)
|
|
@@ -164,7 +164,7 @@ test('etags get stored in cache', async (t) => {
|
|
|
164
164
|
const fastify = Fastify()
|
|
165
165
|
await fastify.register(plugin)
|
|
166
166
|
|
|
167
|
-
fastify.get('/one', (
|
|
167
|
+
fastify.get('/one', (_req, reply) => {
|
|
168
168
|
reply.etag('123456').send({ hello: 'world' })
|
|
169
169
|
})
|
|
170
170
|
|
|
@@ -191,7 +191,7 @@ test('etag cache life is customizable', async (t) => {
|
|
|
191
191
|
const fastify = Fastify()
|
|
192
192
|
await fastify.register(plugin)
|
|
193
193
|
|
|
194
|
-
fastify.get('/one', function (
|
|
194
|
+
fastify.get('/one', function (_req, reply) {
|
|
195
195
|
reply
|
|
196
196
|
// We set a cache lifetime of 50 milliseconds
|
|
197
197
|
.etag('123456', 50)
|
|
@@ -237,7 +237,7 @@ test('returns response payload', async (t) => {
|
|
|
237
237
|
const fastify = Fastify()
|
|
238
238
|
await fastify.register(plugin)
|
|
239
239
|
|
|
240
|
-
fastify.get('/one', (
|
|
240
|
+
fastify.get('/one', (_req, reply) => {
|
|
241
241
|
reply.etag('123456', 300).send({ hello: 'world' })
|
|
242
242
|
})
|
|
243
243
|
|
package/test/headers.test.js
CHANGED
|
@@ -10,7 +10,7 @@ test('decorators get added', async (t) => {
|
|
|
10
10
|
const fastify = Fastify()
|
|
11
11
|
await fastify.register(plugin)
|
|
12
12
|
|
|
13
|
-
fastify.get('/', (
|
|
13
|
+
fastify.get('/', (_req, reply) => {
|
|
14
14
|
t.assert.ok(reply.etag)
|
|
15
15
|
reply.send()
|
|
16
16
|
})
|
|
@@ -31,7 +31,7 @@ test('decorators add headers', async (t) => {
|
|
|
31
31
|
const fastify = Fastify()
|
|
32
32
|
await fastify.register(plugin)
|
|
33
33
|
|
|
34
|
-
fastify.get('/', (
|
|
34
|
+
fastify.get('/', (_req, reply) => {
|
|
35
35
|
reply.etag(tag).send()
|
|
36
36
|
})
|
|
37
37
|
|
|
@@ -51,7 +51,7 @@ test('sets etag header for falsy argument', async (t) => {
|
|
|
51
51
|
const fastify = Fastify()
|
|
52
52
|
await fastify.register(plugin)
|
|
53
53
|
|
|
54
|
-
fastify.get('/', (
|
|
54
|
+
fastify.get('/', (_req, reply) => {
|
|
55
55
|
reply.etag().send()
|
|
56
56
|
})
|
|
57
57
|
|
|
@@ -70,7 +70,7 @@ test('sets no-cache header', async (t) => {
|
|
|
70
70
|
const fastify = Fastify()
|
|
71
71
|
await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
|
|
72
72
|
|
|
73
|
-
fastify.get('/', (
|
|
73
|
+
fastify.get('/', (_req, reply) => {
|
|
74
74
|
reply.send({ hello: 'world' })
|
|
75
75
|
})
|
|
76
76
|
|
|
@@ -95,7 +95,7 @@ test('sets private with max-age header', async (t) => {
|
|
|
95
95
|
const fastify = Fastify()
|
|
96
96
|
await fastify.register(plugin, opts)
|
|
97
97
|
|
|
98
|
-
fastify.get('/', (
|
|
98
|
+
fastify.get('/', (_req, reply) => {
|
|
99
99
|
reply.send({ hello: 'world' })
|
|
100
100
|
})
|
|
101
101
|
|
|
@@ -124,7 +124,7 @@ test('sets public with max-age and s-maxage header', async (t) => {
|
|
|
124
124
|
const fastify = Fastify()
|
|
125
125
|
await fastify.register(plugin, opts)
|
|
126
126
|
|
|
127
|
-
fastify.get('/', (
|
|
127
|
+
fastify.get('/', (_req, reply) => {
|
|
128
128
|
reply.send({ hello: 'world' })
|
|
129
129
|
})
|
|
130
130
|
|
|
@@ -151,12 +151,12 @@ test('do not set headers if another upstream plugin already sets it', async (t)
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
const fastify = Fastify()
|
|
154
|
-
fastify.addHook('onRequest', async function checkCachingDoesNotOverrideCacheControlHeader (
|
|
154
|
+
fastify.addHook('onRequest', async function checkCachingDoesNotOverrideCacheControlHeader (_req, reply) {
|
|
155
155
|
reply.header('cache-control', 'do not override')
|
|
156
156
|
})
|
|
157
157
|
await fastify.register(plugin, opts)
|
|
158
158
|
|
|
159
|
-
fastify.get('/', (
|
|
159
|
+
fastify.get('/', (_req, reply) => {
|
|
160
160
|
reply.send({ hello: 'world' })
|
|
161
161
|
})
|
|
162
162
|
|
|
@@ -182,7 +182,7 @@ test('only sets max-age and ignores s-maxage with private header', async (t) =>
|
|
|
182
182
|
const fastify = Fastify()
|
|
183
183
|
await fastify.register(plugin, opts)
|
|
184
184
|
|
|
185
|
-
fastify.get('/', (
|
|
185
|
+
fastify.get('/', (_req, reply) => {
|
|
186
186
|
reply.send({ hello: 'world' })
|
|
187
187
|
})
|
|
188
188
|
|
|
@@ -210,7 +210,7 @@ test('s-maxage is optional with public header', async (t) => {
|
|
|
210
210
|
const fastify = Fastify()
|
|
211
211
|
await fastify.register(plugin, opts)
|
|
212
212
|
|
|
213
|
-
fastify.get('/', (
|
|
213
|
+
fastify.get('/', (_req, reply) => {
|
|
214
214
|
reply.send({ hello: 'world' })
|
|
215
215
|
})
|
|
216
216
|
|
|
@@ -230,7 +230,7 @@ test('sets no-store with max-age header', async (t) => {
|
|
|
230
230
|
const fastify = Fastify()
|
|
231
231
|
await fastify.register(plugin, { privacy: 'no-store', expiresIn: 300 })
|
|
232
232
|
|
|
233
|
-
fastify.get('/', (
|
|
233
|
+
fastify.get('/', (_req, reply) => {
|
|
234
234
|
reply.send({ hello: 'world' })
|
|
235
235
|
})
|
|
236
236
|
|
|
@@ -255,7 +255,7 @@ test('sets the expires header', async (t) => {
|
|
|
255
255
|
const fastify = Fastify()
|
|
256
256
|
await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
|
|
257
257
|
|
|
258
|
-
fastify.get('/', (
|
|
258
|
+
fastify.get('/', (_req, reply) => {
|
|
259
259
|
reply.expires(now).send({ hello: 'world' })
|
|
260
260
|
})
|
|
261
261
|
|
|
@@ -275,7 +275,7 @@ test('sets the expires header to a falsy value', async (t) => {
|
|
|
275
275
|
const fastify = Fastify()
|
|
276
276
|
await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
|
|
277
277
|
|
|
278
|
-
fastify.get('/', (
|
|
278
|
+
fastify.get('/', (_req, reply) => {
|
|
279
279
|
reply.expires().send({ hello: 'world' })
|
|
280
280
|
})
|
|
281
281
|
|
|
@@ -294,7 +294,7 @@ test('sets the expires header to a custom value', async (t) => {
|
|
|
294
294
|
const fastify = Fastify()
|
|
295
295
|
await fastify.register(plugin, { privacy: plugin.privacy.NOCACHE })
|
|
296
296
|
|
|
297
|
-
fastify.get('/', (
|
|
297
|
+
fastify.get('/', (_req, reply) => {
|
|
298
298
|
reply.expires('foobar').send({ hello: 'world' })
|
|
299
299
|
})
|
|
300
300
|
|
package/types/index.test-d.ts
CHANGED
|
@@ -22,7 +22,7 @@ expectType<AbstractCacheCompliantObject['set']>(fastify.cache.set)
|
|
|
22
22
|
expectType<string>(fastify.cacheSegment)
|
|
23
23
|
// expectType<number>(fastify.etagMaxLife);
|
|
24
24
|
|
|
25
|
-
fastify.get('/one', async (
|
|
25
|
+
fastify.get('/one', async (_request, reply) => {
|
|
26
26
|
expectType<(tag?: string, timeToLive?: number) => FastifyReply>(reply.etag)
|
|
27
27
|
expectType<(date?: Date) => FastifyReply>(reply.expires)
|
|
28
28
|
|
|
@@ -32,7 +32,7 @@ fastify.get('/one', async (request, reply) => {
|
|
|
32
32
|
return { message: 'one' }
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
fastify.get('/two', async (
|
|
35
|
+
fastify.get('/two', async (_request, reply) => {
|
|
36
36
|
expectType<FastifyReply>(
|
|
37
37
|
reply.etag('hello', 6000).expires(new Date(Date.now() + 6000))
|
|
38
38
|
)
|
|
@@ -51,7 +51,7 @@ const badCachingOptions = {
|
|
|
51
51
|
|
|
52
52
|
expectError(shouldErrorApp.register(fastifyCaching, badCachingOptions))
|
|
53
53
|
|
|
54
|
-
fastify.get('/three', async (
|
|
54
|
+
fastify.get('/three', async () => {
|
|
55
55
|
expectAssignable<Promise<unknown>>(
|
|
56
56
|
fastify.cache.get('well-known')
|
|
57
57
|
)
|